Contents
Overview
When writing C# code we often want to do set based operations on Lists, Dictionaries or other IEnumerables. Here I’ll walk you through Except, a Linq extension method that you can use to get objects from one List that don’t exist in another. I’ll also explain how you can use the same approach on dictionaries and I’ll touch on other similar set based methods like Union, Concat and Intersect.
Except
To get the items from list one list (A) that are not in another list (B) you can use the Linq Except method like this:
var a = new List<int>() { 1, 2, 3, 4, 5 }; var b = new List<int>() { 2, 4, 9, 16, 25 }; var aNotB = a.Except(b); aNotB.ToList().ForEach(x => Console.WriteLine(x));
Which will produce the following results:
1
3
5
Except is a Linq extension method, so to use it you must first import System.Linq at the top of your file, like this:
using System.Linq;
It’s worth bearing in mind that the result of A.Except(B) will be an IEnumerable, if you want the result as a list call .ToList() as I’ve done above.
Linq Except is the C# equivalent of using “Where X Not In” in SQL.
Except: Example Use Case
There are all sorts of reasons you might want the values from one list that aren’t in another, for instance:
Say you have a shopping list, but you’ve just been to the store and bought some of the items, you might want to get everything on the original shopping list that you didn’t just purchase:
var shopppingList = new List<string>() { "apples", "beans", "pasta", "butter", "rice", "flour", "eggs" }; var itemsBought = new List<string>() { "chocolate", "butter", "flour", "eggs", "icing sugar" }; var newShoppingList = shopppingList.Except(itemsBought); Console.WriteLine("You still need to buy the following items:"); foreach (var item in newShoppingList) { Console.WriteLine($" {item}"); }
Which outputs:
You still need to buy the following items: apples beans pasta rice
Using Except with Dictionaries (i.e. Get Items Not In Another Dictionary)
Linq Except works for any IEnumerable, so it’s pretty easy to use it to check for items in one dictionary that are not in another dictionary by calling var aExceptB = a.Except(b); as follows:
var a = new Dictionary<int, int>() { { 0, 0 }, { 1, 1 }, { 2, 4 }, { 3, 9 }, { 4, 16 } }; var b = new Dictionary<int, int>() { { 0, 0 }, { 1, 2 }, { 2, 4 }, { 3, 6 } }; var aExceptB = a.Except(b); aExceptB.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: [1, 1] [3, 9] [4, 16] */
By default, this will consider items in the dictionary to match if (and only if) both their keys and values are equal. Eg. [apples, 6] will match [apples, 6] bit won’t match [apples, 4].
Although this is the default behaviour of a C# Dictionary Except, you often just want all the items from Dictionary A where the item’s key doesn’t exist in Dictionary B. You can do this kind of dictionary except as follows:
How to use except with a dictionary just comparing keys (i.e. Using a non-default IEqualityComparer)
One caveat when using Except on dictionaries is that for two items in a dictionary to be considered equal, both the key and the value must be equal. But what if you don’t care what the value is, you just want to compare using keys?
This is where it’s helpful to be able to specify what it means for two things to be equal. To do this we need to implement IEqualityComparer:
private class KeyComparer<T1, T2> : IEqualityComparer<KeyValuePair<T1, T2>> where T1 : IComparable { public bool Equals(KeyValuePair<T1, T2> x, KeyValuePair<T1, T2> y) { //Check whether the keys are equal return x.Key.Equals(y.Key); } // GetHashCode() must return the same value for equal objects. public int GetHashCode(KeyValuePair<T1, T2> kVPair) { return kVPair.Key.GetHashCode(); } } static void Main() { var a = new Dictionary<int, int>() { { 0, 0 }, { 1, 1 }, { 2, 4 }, { 3, 9 }, { 4, 16 } }; var b = new Dictionary<int, int>() { { 0, 0 }, { 1, 2 }, { 2, 4 }, { 3, 6 } }; var aExceptB = a.Except(b, new KeyComparer<int, int>()); aExceptB.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: [4, 16] */ }
The important bit here is return x.Key.Equals(y.Key);
this says that if if element X has the same key as element Y, then consider them to be equal. This means that { 3, 9 }
in dictionary A can match with { 3, 6 }
in Dictionary B because the keys of both match, hence it’s excluded from the output.
Other similar functions
Union
If you want any element that is in either list A or list B, then union might be the way to go:
var a = new List<int>() { 1, 2, 3, 4 }; var b = new List<int>() { 3, 4, 5, 6 }; var aUnionB = a.Union(b); aUnionB.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: 1 2 3 4 5 6 */
However, implicitly does a Distinct on the results, so you won’t get repeated elements just because they’re in both lists. Moreover – I tried it with two 1s in list A and got the same results output – so be warned, this will remove duplicates from your lists.
If you want to keep the duplicates, consider Concat:
Concat
This is a lot like Union, but it will keep the duplicated results. A.Concat(B)
will give you list A concatenated with (joined on to) list B. i.e. you’ll get all the elements in A and all the Elements in B, and any values in both will be duplicated. For example:
var a = new List<int>() { 1, 2, 3, 4 }; var b = new List<int>() { 3, 4, 5, 6 }; var results = a.Concat(b); results.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: 1 2 3 4 3 4 5 6 */
You can see from the output that List b has simple been stuck on the end of List A.
Intersect
So you want the all the items that occur in both list a and list b? You’re looking for the Linq Intersect method. It can be used as follows:
var a = new List<int>() { 1, 2, 3, 3, 4 }; var b = new List<int>() { 3, 4, 5, 6 }; var aIntersectB = a.Intersect(b); aIntersectB.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: 3 4 */
As you can see from the given results, this also does an implicit Distinct, so you will lose and duplicated values in your lists.
Not Intersect?
What if you want any items from list A that aren’t in B and any items from List B that aren’t in A? Well, there isn’t a dedicated Linq method exactly for that… but, you can build it up yourself with the Union and Except methods:
var a = new List<int>() { 1, 2, 3, 4 }; var b = new List<int>() { 3, 4, 5, 6 }; var results = a.Except(b).Union(b.Except(a)); results.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: 1 2 5 6 */
Distinct
I’ve mentioned distinct a few times and it’s worth a mention, but it’s slightly different to the methods so far discussed. So far we’ve looked at methods that take two IEnumerables and return an IEnumerable. Distinct is different in that it takes a single IEnumerable and returns an IEnumerable.
Distinct is used to return the distinct elements from a list, i.e. at most one copy of every element in a list. It can be used as follows:
var a = new List<int>() { 1, 2, 3, 3, 4, 1 }; var results = a.Distinct(); results.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: 1 2 3 4 */
Do These Linq Methods Preserve the Order of Elements?
I wasn’t sure, so I tried it, and yes, these Linq methods do preserve the order of the items in the Lists:
var a = new List<int>() {4, 3, 2, 4, 1 }; var b = new List<int>() { 3, 4, 5, 6 }; var results = a.Union(b); results.ToList().ForEach(x => Console.WriteLine(x)); /* this code outputs: 4 3 2 1 5 6 */
Conclusion
Hopefully this has given you a good introduction to performing set based operations in C# with Linq. In particular using Except to find the items in one list that aren’t in another.
If you’ve spotted a mistake, or if you think there’s something I could add to make this more useful, or if you just want to say “hi” then please leave a message in the comments.
Until next time, have a nice day.