I'm learning with LINQ, but when I don't have a simple list (I can easily do a simple integer list, which is not a problem), I can't figure out how to use Distinct. If you want to use on the object list of one or more properties of an object Distinct , what to do?
Example: if an object is Person, it has a Property Id. How do I get all people and use Distinct and the object's Property Id on them?
Person1: Id=1, Name="Test1" Person2: Id=1, Name="Test1" Person3: Id=2, Name="Test2"
How can I get Person1 and Person3? Is that possible?
If LINQ cannot be implemented, what is the best way to get the Person list based on some properties of Person in. NET 3.5?
#1 building
The following code is functionally equivalent to Jon Skeet's answer .
Tested on. NET 4.5 and can be run on any previous version of LINQ.
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))); }
Accidentally, On Google Code See The latest version of Jon Skeet, distinguishby.cs .
#2 building
If you need to use the Distinct method on multiple properties, you can check out my PowerfulExtensions Library. At present, it is still in its infancy, but you can already use Distinct, Union, Intersect, Except and other methods on many attributes.
This is your usage:
using PowerfulExtensions.Linq; ... var distinct = myArray.Distinct(x => x.A, x => x.B);
#3 building
The best way to be compatible with other. NET versions is to override Equals and GetHash to handle this problem (see Stack Overflow problem). This code returns a different value. However, I want to return a strongly typed collection instead of an anonymous type ), but the solution in this article is useful if you need to use something common throughout your code.
#4 building
When we face such a task in the project, we define a small API to form a comparator.
Therefore, the use case is as follows:
var wordComparer = KeyEqualityComparer.Null<Word>(). ThenBy(item => item.Text). ThenBy(item => item.LangID); ... source.Select(...).Distinct(wordComparer);
The API itself is as follows:
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; }
For more details, please visit our website: IEqualityComparer in LINQ .
#5 building
You can use standard Linq.ToLookup() . This creates a collection of values for each unique key. Just select the first item in the set
Persons.ToLookup(p => p.Id).Select(coll => coll.First());