27

I have two collections of type ICollection<MyType> called c1 and c2. I'd like to find the set of items that are in c2 that are not in c1, where the heuristic for equality is the Id property on MyType.

What is the quickest way to perform this in C# (3.0)?

Sae1962
  • 1,122
  • 15
  • 31
Ben Aston
  • 53,718
  • 65
  • 205
  • 331

3 Answers3

39

Use Enumerable.Except and specifically the overload that accepts an IEqualityComparer<MyType>:

var complement = c2.Except(c1, new MyTypeEqualityComparer());

Note that this produces the set difference and thus duplicates in c2 will only appear in the resulting IEnumerable<MyType> once. Here you need to implement IEqualityComparer<MyType> as something like

class MyTypeEqualityComparer : IEqualityComparer<MyType> {
    public bool Equals(MyType x, MyType y) {
        return x.Id.Equals(y.Id);
    }

    public int GetHashCode(MyType obj) {
        return obj.Id.GetHashCode();
    }
}
jason
  • 236,483
  • 35
  • 423
  • 525
3

If using C# 3.0 + Linq:

var complement = from i2 in c2
                 where c1.FirstOrDefault(i1 => i2.Id == i1.Id) == null
                 select i2;

Loop through complement to get the items.

Kilazur
  • 3,089
  • 1
  • 22
  • 48
Alex LE
  • 20,042
  • 4
  • 30
  • 28
  • 2
    This will use nested loops and is very inefficient. – Samuel Neff Feb 03 '10 at 17:36
  • Maybe it's more efficient to use `where !c1.Any(i1 => i2.Id == i1.Id)` ? In this case you don't retrieve any value in the where condition, you just check for existence of the items. See [DotNetFiddle](https://dotnetfiddle.net/iQwS1v) – Matt Apr 18 '18 at 15:38
0
public class MyTypeComparer : IEqualityComparer<MyType>
{
    public MyTypeComparer()
    {    
    }

    #region IComparer<MyType> Members

    public bool Equals(MyType x, MyType y)
    {
        return string.Equals(x.Id, y.Id);
    }

    public int GetHashCode(MyType obj)
    {
        return base.GetHashCode();
    }

    #endregion     
}

Then, using Linq:

c3 collection = new collection().add(c1);
c3.add(c2);
var items = c3.Distinct(new MyTypeComparer());

You could also do it using generics and predicates. If you need a sample, let me know.

Sae1962
  • 1,122
  • 15
  • 31
FiveTools
  • 5,970
  • 15
  • 62
  • 84
  • 1
    won't Distinct just filter out the duplicates leaving you with c1 union c2, instead of c1- c2? – luke Feb 03 '10 at 17:40