Difference between two lists

c#
226,622

Solution 1

Using Except is exactly the right way to go. If your type overrides Equals and GetHashCode, or you're only interested in reference type equality (i.e. two references are only "equal" if they refer to the exact same object), you can just use:

var list3 = list1.Except(list2).ToList();

If you need to express a custom idea of equality, e.g. by ID, you'll need to implement IEqualityComparer<T>. For example:

public class IdComparer : IEqualityComparer<CustomObject>
{
    public int GetHashCode(CustomObject co)
    {
        if (co == null)
        {
            return 0;
        }
        return co.Id.GetHashCode();
    }

    public bool Equals(CustomObject x1, CustomObject x2)
    {
        if (object.ReferenceEquals(x1, x2))
        {
            return true;
        }
        if (object.ReferenceEquals(x1, null) ||
            object.ReferenceEquals(x2, null))
        {
            return false;
        }
        return x1.Id == x2.Id;
    }
}

Then use:

var list3 = list1.Except(list2, new IdComparer()).ToList();

Note that this will remove any duplicate elements. If you need duplicates to be preserved, it would probably be easiest to create a set from list2 and use something like:

var list3 = list1.Where(x => !set2.Contains(x)).ToList();

Solution 2

You could do something like this:

var result = customlist.Where(p => !otherlist.Any(l => p.someproperty == l.someproperty));

Solution 3

I think important to emphasize - using Except method will return you items who are in the first without the items in the second one only. It does not return those elements in second that do not appear in first.

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2).ToList(); //list3 contains only 1, 2

But if you want get real difference between two lists:

Items who are in the first without the items in the second one and items who are in the second without the items in the first one.

You need using Except twice:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list3 = list1.Except(list2); //list3 contains only 1, 2
var list4 = list2.Except(list1); //list4 contains only 6, 7
var resultList = list3.Concat(list4).ToList(); //resultList contains 1, 2, 6, 7

Or you can use SymmetricExceptWith method of HashSet. But it changes the set on which called:

var list1 = new List<int> { 1, 2, 3, 4, 5};
var list2 = new List<int> { 3, 4, 5, 6, 7 };

var list1Set = list1.ToHashSet(); //.net framework 4.7.2 and .net core 2.0 and above otherwise new HashSet(list1)
list1Set.SymmetricExceptWith(list2);
var resultList = list1Set.ToList(); //resultList contains 1, 2, 6, 7

Solution 4

var third = first.Except(second);

(you can also call ToList() after Except(), if you don't like referencing lazy collections.)

The Except() method compares the values using the default comparer, if the values being compared are of base data types, such as int, string, decimal etc.

Otherwise the comparison will be made by object address, which is probably not what you want... In that case, make your custom objects implement IComparable (or implement a custom IEqualityComparer and pass it to the Except() method).

Solution 5

var list3 = list1.Where(x => !list2.Any(z => z.Id == x.Id)).ToList();

Note: list3 will contain the items or objects that are not in both lists. Note: Its ToList() not toList()

Share:
226,622

Related videos on Youtube

Admin
Author by

Admin

Updated on February 17, 2022

Comments

  • Admin
    Admin about 2 years

    I Have two generic list filled with CustomsObjects.

    I need to retrieve the difference between those two lists(Items who are in the first without the items in the second one) in a third one.

    I was thinking using .Except() was a good idea but I don't see how to use this.. Help!

  • digEmAll
    digEmAll about 13 years
    Just as an aside, I'd add that Except is a Set operation, then the resulting list will have distinct values, e.g {'A','A','B','C'}.Except({'B','C'}) returns {'A'}
  • Jim Mischel
    Jim Mischel about 13 years
    Enumerable.Except internally uses HashSet or something similar. It definitely does not use the naive O(n^2) algorithm.
  • Jim Mischel
    Jim Mischel about 13 years
    And if your lists each contain a million items, you're going to wait a long time. Use IEnumerable.Except instead.
  • foson
    foson about 13 years
    @Jim Mischel: You are right, Enumerable.Except uses an internal Set data structure and adds items from both IEnumerables into the Set. Wish the documentation would say something about that.
  • alice7
    alice7 about 12 years
    it saved me one foreach loop.
  • Donvini
    Donvini over 11 years
    Shouldn't one of those "l.someproperty" be "p.someproperty"?
  • Marnee KG7SIO
    Marnee KG7SIO over 8 years
    Yep. I only have about 450 items and I'm still waiting. Be careful using this.
  • MrLister
    MrLister over 7 years
    I have this: {'A','A','B','C'}.Except({'B','C'}) returns {'A'} which works... however {'B','C'}.Except({'A','B','C'}) DOES NOT return {'A'}
  • Dhanuka777
    Dhanuka777 over 7 years
    This can be simplified with customlist.Where(p => otherlist.Any(l => p.someproperty != l.someproperty));
  • Steve Hibbert
    Steve Hibbert about 7 years
    The set difference of two sets is defined as the members of the first set that do not appear in the second set (quoting MS). So, {B,C}.Except({A,B,C}) should return nothing as B and C are both in the second set. Not a bug, more to do with the mathematical definition of the function.
  • Ehsan Sajjad
    Ehsan Sajjad over 5 years
    So @JonSkeet if i want to compare two lists on the basis of let's say 2 properties i can write this way comparer and get the items that are not equal right?
  • Jon Skeet
    Jon Skeet over 5 years
    @EhsanSajjad: Well you'd get the items from list A that aren't in list B or vice versa.
  • Mitch Wheat
    Mitch Wheat over 5 years
    6 years after accepted answer and adds nothing new of value
  • Teezy7
    Teezy7 about 5 years
    Note: Result List is the items that exist in checklist but not in myList
  • rsenna
    rsenna about 5 years
    That's helpful. Just be aware the OP made it clear they did not want full difference: "I need to retrieve the difference between those two lists (Items who are in the first without the items in the second one)".
  • Murphybro2
    Murphybro2 almost 5 years
    @Dhanuka777 that's incorrect. The Any should be an All if you want to do it like that. This is because the second list could have the item from the first list, but if it's not the first one checked then it will immediately get a true for p.someproperty != l.someproperty. This results in items being returned that exist in both lists. Shame on the 6 people who upvoted this.
  • NetMage
    NetMage almost 5 years
    Years later, I would point out that if list2 has duplicates converting it to a set doesn't work properly, if what you want is the difference between lists.
  • Jon Skeet
    Jon Skeet almost 5 years
    @NetMage: The OP stated they want "Items who are in the first without the items in the second one" - that sounds like a set difference to me. If the first list contains { 5, 5, 5, 5, 1 } and the second list contains { 5 } then only 1 is in the first list but not the second.