Search code examples
c#arrayslinqintersection

Intersect between two List<int[]>


I want to get the intersection of the following two lists.

List<int[]> list1 = new List<int[]>
{
    new int[] { 0, 0, 0, },
    new int[] { 1, 1, 1, }
};

List<int[]> list2 = new List<int[]>
{
    new int[] { 1, 1, 1, },
    new int[] { 2, 2, 2, }
};

But when I try

List<int[]> intersection = list1.Intersect(list2).ToList();

The intersection returns empty. When I try this exact setup with two List<int> (not array) it works as expected.

This is my first time using Linq. How can I get this intersection?


Solution

  • The default comparer used for checking equality is a reference comparison.
    This default is not suited for comparing the content of your arrays.

    You can achieve what you need by using a custom comparer (derived from IEqualityComparer), that actually compares the content of the arrays:

    // Custom comparer:
    class MyComparer : IEqualityComparer<int[]>
    {
        public bool Equals(int[] a, int[] b) 
        {
            if (ReferenceEquals(a, b)) return true; 
            if (a is null) return false; 
            if (b is null) return false;
            return a.SequenceEqual(b); 
        }
        public int  GetHashCode(int[] a)     
        { 
            return a.Aggregate(1, (acc, item) => acc * item); 
        }
    }
    

    Then you can use for the intersection:

    List<int[]> list1 = new List<int[]>
                {
                    new int[] { 0, 0, 0, },
                    new int[] { 1, 1, 1, }
                };
    
    List<int[]> list2 = new List<int[]>
                {
                    new int[] { 1, 1, 1, },
                    new int[] { 2, 2, 2, }
                };
    
    // Intersection with custom comparer:
    //------------------------------------------------vvvvvvvvvvvvvvvv----------
    List<int[]> intersection = list1.Intersect(list2, new MyComparer()).ToList();
    
    foreach (var list in intersection)
    {
        Console.WriteLine("{0}", string.Join(", ", list));
    }
    

    Output:

    1, 1, 1
    

    Note:
    My implementation of GetHashCode is just a trivial one you might need to refine.
    You can see @Oliver's comment below regarding how to improve it (it requires the Microsoft.Bcl.HashCode NuGet for HashCode).
    Another issue with my GetHashCode is that it requires to traverse the whole array. It can also overflow especially (easily) when the arrays are long. As commented by @Dmitry Bychenko, you can use the following instead (also requires HashCode from Microsoft.Bcl.HashCode):

    return HashCode.Combine(a.DefaultIfEmpty().Take(4)); 
    

    This will take at most 4 items into consideration and let .Net combine them.