17

I have two list of members like this:

Before: Peter, Ken, Julia, Tom

After: Peter, Robert, Julia, Tom

As you can see, Ken is is out and Robert is in.

What I want is to detect the changes. I want a list of what has changed in both lists. How can linq help me?

animuson
  • 53,861
  • 28
  • 137
  • 147
Salo
  • 171
  • 1
  • 3

4 Answers4

29

Your question is not completely specified but I assume that you are looking for the differences as sets (that is, ordering does not matter). If so, you want the symmetric difference of the two sets. You can achieve this using Enumerable.Except:

before.Except(after).Union(after.Except(before));
Will
  • 340
  • 3
  • 8
jason
  • 236,483
  • 35
  • 423
  • 525
22

As an alternative to linq answers, which have to pass both lists twice, use HashSet.SymmetricExceptWith():

var difference = new HashSet(before);
difference.SymmetricExceptWith(after);

Could be considerably more efficient.

Nigel Touch
  • 1,946
  • 2
  • 19
  • 12
5

Another way:

before.Union(after).Except(before.Intersect(after))
Manu
  • 28,753
  • 28
  • 75
  • 83
2

Here is the version having O(n) complexity, provided your sequences are both ordered:

    public static IEnumerable<T> SymmetricDifference<T>(IEnumerable<T> coll1, IEnumerable<T> coll2, IComparer<T> cmp)
    {
        using (IEnumerator<T> enum1 = coll1.GetEnumerator())
        using (IEnumerator<T> enum2 = coll2.GetEnumerator())
        {
            bool enum1valid = enum1.MoveNext();
            bool enum2valid = enum2.MoveNext();
            while (enum1valid && enum2valid)
            {
                int cmpResult = cmp.Compare(enum1.Current, enum2.Current);
                if (cmpResult < 0)
                {
                    yield return enum1.Current;
                    enum1valid = enum1.MoveNext();
                }
                else if (cmpResult > 0)
                {
                    yield return enum2.Current;
                    enum2valid = enum2.MoveNext();
                }
                else
                {
                    enum1valid = enum1.MoveNext();
                    enum2valid = enum2.MoveNext();
                }
            }
            while (enum1valid)
            {
                yield return enum1.Current;
                enum1valid = enum1.MoveNext();
            }
            while (enum2valid)
            {
                yield return enum2.Current;
                enum2valid = enum2.MoveNext();
            }
        }
    }


    public static IEnumerable<T> SymmetricDifference<T>(IEnumerable<T> coll1, IEnumerable<T> coll2)
    {
        return SymmetricDifference(coll1, coll2, Comparer<T>.Default);
    }
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
SergeyS
  • 3,909
  • 2
  • 15
  • 17
  • 1
    This would work only for sorted sequences, right? One cannot make symdiff in just O(n) for arbitrary sequences. – Vlad Sep 04 '12 at 13:55
  • Sure it does, just look at the co-iteration. It is slightly modified version of sequential-merge from merge-sort. Although it will not work for unsorted, it is _very_ efficient for sorted, so in many cases it will be faster and more memory-friendly to pre-sort the collection and then use this filter ~> n + 2*(n log n) instead of some naiive all-vs-all n^2 or some hashtable memory hogs ..in case of _many_ elements of course:) – quetzalcoatl Jan 18 '13 at 16:07