One of the programming scenarios we can encounter once in a while is checking whether certain items in a list exist in another. This article is going to dive deep into all the techniques to compare lists. We are also going to compare how these techniques perform and discuss some of the best practices and considerations that can make our solutions as efficient as possible.
Without further ado, let’s start!
Use Iteration to Check if Items Exist in Another List in C#
We can use iteration to check whether items exist in another list in C#:
foreach (var item in firstList) { if (secondList.Contains(item)) { return true; } } return false;
Assuming we have two lists firstList
and secondList
, we loop through firstList
as we check whether any element exists in secondList
and exit the loop if we find a match.
For a complex type like a Student
class with properties such as Id
, FirstName
and LastName
, we can use the same technique to check for common Student
names:
foreach (var student in firstList) { if (secondList.Any(s => s.FirstName == student.FirstName && s.LastName == student.LastName)) { return true; } } return false;
How to Use Intersect to Verify the Existence of Items in Another List
Besides the use of iteration, we can leverage the use of the Intersect()
method, to check whether list items exist in another list in C#. This method returns common elements in two lists, ideally performing an intersection between two lists:
return firstList.Intersect(secondList).Any();
Likewise, using our Student
class example, we can invoke the Intersect()
method to check whether two lists share common students:
return firstList .Select(student => $"{student.FirstName} {student.LastName}") .Intersect(secondList.Select(student => $"{student.FirstName} {student.LastName}")) .Any();
Check if Items of a List Exist in Another List in C# Using Any
We can use the IEnumerable.Any()
method to check whether items of one list exist in another. The method works by checking whether any element of our lists exists or satisfies some conditions.
Let’s learn how we can achieve our goal with an example:
return secondList.Any(firstList.Contains);
In this case, the method returns true
if secondList
shares common elements with firstList
. For our Student
class, we can check if two List<Student>
objects share common students by passing our condition while invoking the Any()
method:
return secondList.Any(student => firstList.Any(s => s.FirstName == student.FirstName && s.LastName == student.LastName));
Verify the Existence of Items in Another List Using Except
Besides the other techniques, we can use the Except()
method to check whether two lists share common elements. As the name suggests, the method retrieves elements from one list that are not in another and returns an IEnumerable<T>
object.
Let’s implement an example to learn how it works:
return secondList.Except(firstList).Count() != secondList.Count;
Here, we invoke the Except()
method to get the elements not in secondList
. If the count of those elements equals the items in secondList
, then the two lists do not share common elements. When dealing with complex types like our Student
class, we can use the same strategy to check whether two List<Student>
objects share the same elements:
return secondList .Select(student => $"{student.FirstName} {student.LastName}") .Except(firstList.Select(student => $"{student.FirstName} {student.LastName}")) .Count() != secondList.Count;
Check if Items of a List Exist in Another List Using Where in C#
We can use the Where()
LINQ operator to check whether two lists share common elements as it checks for elements that meet specific criteria:
return secondList.Where(firstList.Contains).Any();
In this case, our expression combines the Where()
clause with Contains()
to check if secondList
and firstList
share common elements before returning true
or false
.
Finally, let’s see how we can use the Where()
clause to check if our List<Student>
objects share common items:
return secondList .Any(student => firstList.Any(s => s.FirstName == student.FirstName && s.LastName == student.LastName));
Performance Analysis
Let’s analyze how long each method compares pairs of 1,000,000 lists. We are going to test how each method performs in different scenarios. The first scenario compares an ordered list against a reversed list, while the other compares two lists with different elements, which we populate with different numbers. Finally, the last scenario will have two lists that share a common middle element.
Finally, let’s examine how each technique performs:
| Method | listName | Mean | Rank | Allocated | |---------------------------- |-----------|------------------|------|------------| | CompareListUsingIteration | Reversed | 186.2 μs | 1 | 2 B | | CompareListUsingAnyContains | Reversed | 186.9 μs | 2 | 106 B | | CompareListUsingWhereAny | Reversed | 187.5 μs | 3 | 138 B | | CompareListUsingIntersect | Reversed | 11,964.2 μs | 4 | 18604582 B | | CompareListUsingExcept | Reversed | 21,936.6 μs | 5 | 18604960 B | | CompareListUsingIntersect | Middle | 20,363.9 μs | 1 | 18606100 B | | CompareListUsingExcept | Middle | 33,473.9 μs | 2 | 57180026 B | | CompareListUsingWhereAny | Middle | 90,303,896.7 μs | 3 | 506008 B | | CompareListUsingAnyContains | Middle | 92,659,048.3 μs | 4 | 523240 B | | CompareListUsingIteration | Middle | 93,294,658.7 μs | 5 | 523104 B | | CompareListUsingIntersect | Exclusive| 18,955.2 μs | 1 | 18603878 B | | CompareListUsingExcept | Exclusive| 33,185.8 μs | 2 | 57180763 B | | CompareListUsingIteration | Exclusive| 181,602,156.8 μs | 3 | 1040064 B | | CompareListUsingWhereAny | Exclusive| 181,822,515.7 μs | 4 | 1045976 B | | CompareListUsingAnyContains | Exclusive| 182,225,612.5 μs | 5 | 1045944 B |
Our iteration implementation performs well when comparing an ordered list and its reversed counterpart, as the solution is optimized for the common element case, breaking out of the loop as soon as a matching element is encountered. This also applies to the WhereAny() and AnyContains() methods. However, because these are O(N^2) solutions, their performance decreases as the number of uncommon elements increases.
The Except() method performs very consistently regardless of the data, as it internally creates a HashSet<T>, leading to an O(N) solution. However, it lags behind our Intersect() method, requiring a complete pass of the excepted list to compare the final length.
The Intersect() method, also O(N), is the ideal solution in cases where we cannot know the distribution of common elements. While this solution does have an additional memory cost due to the internal creation of a HashSet<T>, this tradeoff results in higher performance in the general case.
Conclusion
In this article, we learn techniques to check if items exist in another list in C# and how they perform. Which technique did you find the most useful? Let’s discuss more in the comments section below.