Difference between two lists
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()
Related videos on Youtube
Admin
Updated on February 17, 2022Comments
-
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 about 13 yearsJust 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 about 13 years
Enumerable.Except
internally usesHashSet
or something similar. It definitely does not use the naive O(n^2) algorithm. -
Jim Mischel about 13 yearsAnd if your lists each contain a million items, you're going to wait a long time. Use
IEnumerable.Except
instead. -
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 about 12 yearsit saved me one foreach loop.
-
Donvini over 11 yearsShouldn't one of those "l.someproperty" be "p.someproperty"?
-
Marnee KG7SIO over 8 yearsYep. I only have about 450 items and I'm still waiting. Be careful using this.
-
MrLister over 7 yearsI 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 over 7 yearsThis can be simplified with customlist.Where(p => otherlist.Any(l => p.someproperty != l.someproperty));
-
Steve Hibbert about 7 yearsThe 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 over 5 yearsSo @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 over 5 years@EhsanSajjad: Well you'd get the items from list A that aren't in list B or vice versa.
-
Mitch Wheat over 5 years6 years after accepted answer and adds nothing new of value
-
Teezy7 about 5 yearsNote: Result List is the items that exist in checklist but not in myList
-
rsenna about 5 yearsThat'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 almost 5 years@Dhanuka777 that's incorrect. The
Any
should be anAll
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 forp.someproperty != l.someproperty
. This results in items being returned that exist in both lists. Shame on the 6 people who upvoted this. -
NetMage almost 5 yearsYears 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 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.