Three LINQ set methods: Intersect, Union, and Except
Tags: csharp, beginners, tutorial, dotnet
Published: 2022-09-12 14:56:54

_I originally published this post on [my blog](https://canro91.github.io/2022/08/22/IntersectUnionAndExcept/) a couple of weeks ago. It's part of a post series about LINQ._

So far we have covered some of the [most common LINQ methods](https://canro91.github.io/2022/05/16/LINQMethodsInPictures/). This time let's cover three LINQ methods that work like set operations: Intersect, Union, and Except. Probably, we won't use these methods every day, but they will come in handy from time to time.

Let's use our catalog of movies from previous posts in this series to show how these three methods work.

## 1. Intersect

**The Intersect() method finds the common elements between two collections.**

Let's find the movies we both have watched and rated in our catalogs.

```csharp
var mine = new List<Movie>
{
    // We have not exactly a tie here...
    new Movie("Terminator 2", 1991, 4.7f),
    //        ^^^^^^^^^^^^^^
    new Movie("Titanic", 1998, 4.5f),
    new Movie("The Fifth Element", 1997, 4.6f),
    new Movie("My Neighbor Totoro", 1988, 5)
    //        ^^^^^^^^^^^^^^^^^^^^
};

var yours = new List<Movie>
{
    new Movie("My Neighbor Totoro", 1988, 5f),
    //        ^^^^^^^^^^^^^^^^^^^^
    new Movie("Pulp Fiction", 1994, 4.3f),
    new Movie("Forrest Gump", 1994, 4.3f),
    // We have not exactly a tie here...
    new Movie("Terminator 2", 1991, 5f)
    //        ^^^^^^^^^^^^^^
};

var weBothHaveSeen = mine.Intersect(yours);
Console.WriteLine("We both have seen:");
PrintMovies(weBothHaveSeen);

// Output:
// We both have seen:
// My Neighbor Totoro

Console.ReadKey();

static void PrintMovies(IEnumerable<Movie> movies)
{
	  Console.WriteLine(string.Join(",", movies.Select(movie => movie.Name)));
}

record Movie(string Name, int ReleaseYear, float Rating);
```

Notice that, this time, we have two lists of movies, `mine` and `yours`, with the ones I've watched and the ones I guess you have watched, respectively. Also, notice we both have watched "My Neighbor Totoro" and "Terminator 2."

To find the movies we both have seen (the intersection between our two catalogs), we used the `Intersect()` method. But, our example only shows "My Neighbor Totoro." What happened here?

If we pay close attention, we both have watched "Terminator 2," but we gave it different ratings. Since we're using [records from C# 9.0](https://canro91.github.io/2021/09/13/TopNewCSharpFeatures/), records have member-wise comparison. Therefore, our two "Terminator 2" instances aren't exactly the same, even though they have the same name and release year. That's why `Intersect()` doesn't return it.

<figure>
<img src="https://source.unsplash.com/fyaTq-fIlro/600x400" alt="Pile of boxes in a storage room" />

<figcaption>Good luck finding the intersection and union of those boxes...Photo by <a href="https://unsplash.com/@chuttersnap?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">CHUTTERSNAP</a> on <a href="https://unsplash.com/s/photos/boxes?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></figcaption>
</figure>

## 2. Union

**The Union() method finds the elements from both collections without duplicates.**

Let's find all the movies we have in our catalogs.

```csharp
var mine = new List<Movie>
{
    new Movie("Terminator 2", 1991, 5f),
    //        ^^^^^^^^^^^^^^
    new Movie("Titanic", 1998, 4.5f),
    new Movie("The Fifth Element", 1997, 4.6f),
    new Movie("My Neighbor Totoro", 1988, 5f)
    //        ^^^^^^^^^^^^^^^^^^^^
};

var yours = new List<Movie>
{
    new Movie("My Neighbor Totoro", 1988, 5f),
    //        ^^^^^^^^^^^^^^^^^^^^
    new Movie("Pulp Fiction", 1994, 4.3f),
    new Movie("Forrest Gump", 1994, 4.3f),
    new Movie("Terminator 2", 1991, 5f)
    //        ^^^^^^^^^^^^^^
};

var allTheMoviesWeHaveSeen = mine.Union(yours);
Console.WriteLine("All the movies we have seen:");
PrintMovies(allTheMoviesWeHaveSeen);

// Output:
// All the movies we have seen:
// Terminator 2,Titanic,The Fifth Element,My Neighbor Totoro,Pulp Fiction,Forrest Gump

Console.ReadKey();

static void PrintMovies(IEnumerable<Movie> movies)
{
	  Console.WriteLine(string.Join(",", movies.Select(movie => movie.Name)));
}

record Movie(string Name, int ReleaseYear, float Rating);
```

This time we gave the same rating to our shared movies: "Terminator 2" and "My Neighbor Totoro." And, `Union()` showed all the movies from both collections, showing duplicates only once. It works the same way as the union operation in our Math classes.

## 3. Except

**The Except() method finds the elements in one collection that are not present in another one.**

This time, let's find the movies only I have watched.

```csharp
var mine = new List<Movie>
{
    new Movie("Terminator 2", 1991, 5f),
    new Movie("Titanic", 1998, 4.5f),
    //         ^^^^^^^
    new Movie("The Fifth Element", 1997, 4.6f),
    //         ^^^^^^^^^^^^^^^^^
    new Movie("My Neighbor Totoro", 1988, 5f)
};

var yours = new List<Movie>
{
    new Movie("My Neighbor Totoro", 1988, 5f),
    new Movie("Pulp Fiction", 1994, 4.3f),
    new Movie("Forrest Gump", 1994, 4.3f),
    new Movie("Terminator 2", 1991, 5f)
};

var onlyIHaveSeen = mine.Except(yours);
Console.WriteLine();
Console.WriteLine("Only I have seen:");
PrintMovies(onlyIHaveSeen);

// Output:
// Only I have seen:
// Titanic,The Fifth Element

Console.ReadKey();

static void PrintMovies(IEnumerable<Movie> movies)
{
	  Console.WriteLine(string.Join(",", movies.Select(movie => movie.Name)));
}

record Movie(string Name, int ReleaseYear, float Rating);
```

With `Except()`, we found the movies in `mine` that are not in `yours`. When working with `Except()`, we should pay attention to the order of the collection because `mine.Except(yours)` is not the same as `yours.Except(mine)`.

Voilà! These are the `Intersect()`, `Union()`, and `Except()` methods. They work like the Math set operations: intersection, union, and symmetrical difference, respectively. Of the three, I'd say `Except` is the most common method.

LINQ has a similar method to "combine" two collections into a single one: `Concat()`. But, unlike `Union()`, `Concat()` returns all elements from both collections without removing the duplicated ones.

.NET 6 also has a `IntersectBy()`, `UnionBy()`, and `ExceptBy()` methods that work with a keySelector to pick which property to use as the one to intersect, union, or except by. With `IntersectBy()`, for our first example, we can get "Terminator 2" back, even without having exactly the same record in `mine` and `yours`.

***

_Hey! I'm Cesar, a software engineer and lifelong learner. If you want to learn more about this subject, check my course [Getting Started with LINQ](https://bit.ly/csarag-linq-course) on Educative where I cover other LINQ methods in depth._

_Happy coding!_