23 September 2010

LINQ: Distinct with projection

In recent blog post Ed suggested a few useful extension methods missing in LINQ.

Drawback of that implementation is the need to specify lambda taking two arguments in order to create a comparer.

Here's a simpler approach.

First of all, usage:
values.Distinct(v => v.Id)

implementation:

public static class EnumerableExtensions
{
    public static IEnumerable<T> Distinct<T, TId>(this IEnumerable<T> values, Func<T, TId> projection)
        where TId : IEquatable<TId>
    {
        return values.Distinct(new ProjectingEqualityComparer<T, TId>(projection));
    }

    class ProjectingEqualityComparer<T, TId> : IEqualityComparer<T>
        where TId : IEquatable<TId>
    {
        readonly Func<T, TId> projection;

        public ProjectingEqualityComparer(Func<T, TId> projection)
        {
            this.projection = projection;
        }

        public bool Equals(T x, T y)
        {
            return EqualityComparer<TId>.Default.Equals(projection(x), projection(y));
        }

        public int GetHashCode(T obj)
        {
            return EqualityComparer<TId>.Default.GetHashCode(projection(obj));
        }
    }
}

No comments: