LINQ在特定属性上的Distinct()

我正在使用LINQ进行学习,可是当我没有简单的列表时(我很容易作到简单的整数列表,这不是问题),我没法弄清楚如何使用Distinct。 若是要在对象的一个多个属性的对象列表上使用Distinct ,该怎么办? web

示例:若是一个对象是Person ,具备Property Id 。 如何获取全部Person并在其上使用Distinct和对象的属性Id学习

Person1: Id=1, Name="Test1"
Person2: Id=1, Name="Test1"
Person3: Id=2, Name="Test2"

我怎样才能获得Person1和Person3? 那可能吗? 测试

若是LINQ没法实现,那么根据.NET 3.5中Person某些属性来获取Person列表的最佳方法是什么? 网站


#1楼

如下代码在功能上等同于Jon Skeet的答案this

在.NET 4.5上进行了测试,能够在任何早期版本的LINQ上运行。 google

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
  this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
  HashSet<TKey> seenKeys = new HashSet<TKey>();
  return source.Where(element => seenKeys.Add(keySelector(element)));
}

偶然地, 在Google Code上查看Jon Skeet的DistinctBy.cs最新版本spa


#2楼

若是须要在多个属性上使用Distinct方法,则能够签出个人PowerfulExtensions库。 当前它还处于起步阶段,可是您已经能够在许多属性上使用Distinct,Union,Intersect,Except等方法。 code

这是您的用法: 对象

using PowerfulExtensions.Linq;
...
var distinct = myArray.Distinct(x => x.A, x => x.B);

#3楼

与其余.NET版本兼容的最佳方法是重写Equals和GetHash来处理此问题(请参阅Stack Overflow问题。 此代码返回不一样的值。可是,我要返回的是强类型集合,而不是匿名类型 ),可是若是您须要在整个代码中使用通用的东西,那么本文中的解决方案很是有用。 blog


#4楼

当咱们在项目中面对这样的任务时,咱们定义了一个小的API来构成比较器。

所以,用例是这样的:

var wordComparer = KeyEqualityComparer.Null<Word>().
    ThenBy(item => item.Text).
    ThenBy(item => item.LangID);
...
source.Select(...).Distinct(wordComparer);

API自己以下所示:

using System;
using System.Collections;
using System.Collections.Generic;

public static class KeyEqualityComparer
{
    public static IEqualityComparer<T> Null<T>()
    {
        return null;
    }

    public static IEqualityComparer<T> EqualityComparerBy<T, K>(
        this IEnumerable<T> source,
        Func<T, K> keyFunc)
    {
        return new KeyEqualityComparer<T, K>(keyFunc);
    }

    public static KeyEqualityComparer<T, K> ThenBy<T, K>(
        this IEqualityComparer<T> equalityComparer,
        Func<T, K> keyFunc)
    {
        return new KeyEqualityComparer<T, K>(keyFunc, equalityComparer);
    }
}

public struct KeyEqualityComparer<T, K>: IEqualityComparer<T>
{
    public KeyEqualityComparer(
        Func<T, K> keyFunc,
        IEqualityComparer<T> equalityComparer = null)
    {
        KeyFunc = keyFunc;
        EqualityComparer = equalityComparer;
    }

    public bool Equals(T x, T y)
    {
        return ((EqualityComparer == null) || EqualityComparer.Equals(x, y)) &&
                EqualityComparer<K>.Default.Equals(KeyFunc(x), KeyFunc(y));
    }

    public int GetHashCode(T obj)
    {
        var hash = EqualityComparer<K>.Default.GetHashCode(KeyFunc(obj));

        if (EqualityComparer != null)
        {
            var hash2 = EqualityComparer.GetHashCode(obj);

            hash ^= (hash2 << 5) + hash2;
        }

        return hash;
    }

    public readonly Func<T, K> KeyFunc;
    public readonly IEqualityComparer<T> EqualityComparer;
}

更多详细信息,请访问咱们的网站: LINQ中的IEqualityComparer


#5楼

您可使用标准的Linq.ToLookup() 。 这将为每一个惟一键建立一个值集合。 只需选择集合中的第一项

Persons.ToLookup(p => p.Id).Select(coll => coll.First());