Search code examples
c#linqlistwhere-in

how to select common elements according to the value of a property?


If I want the common elements in two list, I can use the intersect function:

var listC = listA.Intersect(listB);

But this compare objects. If the lists have objects of type Persons and I would like to get the persons with the same name for example, how could I do that? Where I set the condition of the name property?

Thanks.


Solution

  • Pass it a custom IEqualityComparer<T>.

    First, make a class that implements that interface:

    public class PersonNameEqualityComparer:IEqualityComparer<Person>
    {
        public int GetHashCode (Person obj)
        {
            return obj.Name.GetHashcode ();
        }
        public bool Equals (Person x, Person y)
        {
            return x.Name == y.Name;
        }
    }
    

    Then, all you need to do is pass an instance of that IEqualityComparer to the intersect method.

    var result = listA.Intersect(listB, new PersonNameEqualityComparer());
    

    You could extend this to any object and any property, using generics and lambdas:

    public class PropertyEqualityComparer<TObject, TProperty> : IEqualityComparer<TObject>
    {
        Func<TObject, TProperty> _selector;
        IEqualityComparer<TProperty> _internalComparer;
        public PropertyEqualityComparer(Func<TObject, TProperty> propertySelector, IEqualityComparer<TProperty> innerEqualityComparer = null)
        {
            _selector = propertySelector;
            _internalComparer = innerEqualityComparer;
        }
        public int GetHashCode(TObject obj)
        {
            return _selector(obj).GetHashCode();
        }
        public bool Equals(TObject x, TObject y)
        {
            IEqualityComparer<TProperty> comparer = _internalComparer ?? EqualityComparer<TProperty>.Default;
            return comparer.Equals(_selector(x), _selector(y));
        }
    }
    

    You could then just use it like this:

    var result = listA.Intersect(listB, new PropertyEqualityComparer<Person, string>(p => p.Name));
    

    or like this:

    var result = listA.Intersect(listB, new PropertyEqualityComparer<Person, string>(p => p.Age));
    

    and so on.