LINQ: Enhancing Distinct With The PredicateEqualityComparer
Today I was writing a LINQ query and I needed to select distinct values based on a comparison criteria.
Fortunately, LINQ’s Distinct method allows an equality comparer to be supplied, but, unfortunately, sometimes, this means having to write custom equality comparer.
Because I was going to need more than one equality comparer for this set of tools I was building, I decided to build a generic equality comparer that would just take a custom predicate. Something like this:
public class PredicateEqualityComparer<T> : EqualityComparer<T> { private Func<T, T, bool> predicate;<span style="color: blue">public </span>PredicateEqualityComparer(<span style="color: #2b91af">Func</span><T, T, <span style="color: blue">bool</span>> predicate) : <span style="color: blue">base</span>() { <span style="color: blue">this</span>.predicate = predicate; } <span style="color: blue">public override bool </span>Equals(T x, T y) { <span style="color: blue">if </span>(x != <span style="color: blue">null</span>) { <span style="color: blue">return </span>((y != <span style="color: blue">null</span>) && <span style="color: blue">this</span>.predicate(x, y)); } <span style="color: blue">if </span>(y != <span style="color: blue">null</span>) { <span style="color: blue">return false</span>; } <span style="color: blue">return true</span>; } <span style="color: blue">public override int </span>GetHashCode(T obj) { <span style="color: green">// Always return the same value to force the call to IEqualityComparer<T>.Equals</span> <span style="color: blue">return </span>0; }
}
Now I can write code like this:
.Distinct(new PredicateEqualityComparer<Item>((x, y) => x.Field == y.Field))
But I felt that I’d lost all conciseness and expressiveness of LINQ and it doesn’t support anonymous types. So I came up with another Distinct extension method:
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> predicate) { return source.Distinct(new PredicateEqualityComparer<TSource>(predicate)); }
And the query is now written like this:
.Distinct((x, y) => x.Field == y.Field)
Looks a lot better, doesn’t it? And it works wit anonymous types.
Update: I, accidently, had published the wrong version of the IEqualityComparer<T>.Equals method,