IEnumerable
collection comparison
C#
LINQ
programming tutorial

Check if one IEnumerable contains all elements of another IEnumerable

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Checking whether one IEnumerable contains all elements of another sounds simple, but there are two different meanings. Sometimes you mean set-style containment, where duplicates do not matter. Other times you mean multiset containment, where counts matter too. The right implementation depends on which of those you actually need.

Set-Style Containment

If duplicates do not matter, a straightforward LINQ answer is:

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5IEnumerable<int> source = new[] { 1, 2, 3, 4, 5 };
6IEnumerable<int> required = new[] { 2, 4 };
7
8bool containsAll = required.All(source.Contains);
9Console.WriteLine(containsAll);

This reads nicely, but it is not always the most efficient solution. source.Contains may scan the source sequence repeatedly, which can become expensive for large collections.

Use a HashSet for Better Performance

If you are doing many membership checks, materialize the source once:

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5IEnumerable<int> source = new[] { 1, 2, 3, 4, 5 };
6IEnumerable<int> required = new[] { 2, 4 };
7
8var sourceSet = new HashSet<int>(source);
9bool containsAll = required.All(sourceSet.Contains);
10
11Console.WriteLine(containsAll);

This is the usual best choice for set-style containment because membership checks are fast and the intent is still clear.

An Alternative with Except

Another readable set-style option is:

csharp
bool containsAll = !required.Except(source).Any();

This works because Except removes values that are present in source. If anything remains, then source was missing something from required.

It is elegant, but it still uses set semantics, so duplicates are ignored.

When Duplicate Counts Matter

Suppose:

  • 'source is [1, 1, 2]'
  • 'required is [1, 1, 1]'

Set-style containment would say “yes, 1 exists,” but multiset containment should say “no, there are not enough ones.” If counts matter, use grouping:

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5IEnumerable<int> source = new[] { 1, 1, 2 };
6IEnumerable<int> required = new[] { 1, 1, 1 };
7
8var sourceCounts = source
9    .GroupBy(x => x)
10    .ToDictionary(g => g.Key, g => g.Count());
11
12bool containsAllWithCounts = required
13    .GroupBy(x => x)
14    .All(g => sourceCounts.TryGetValue(g.Key, out var count) && count >= g.Count());
15
16Console.WriteLine(containsAllWithCounts);

This is the correct answer when multiplicity is part of the problem.

Custom Equality Comparers

If the elements are custom objects, equality becomes important. Both HashSet and LINQ set operations depend on either:

  • a correct Equals and GetHashCode
  • or a custom IEqualityComparer<T>

For example:

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5var comparer = StringComparer.OrdinalIgnoreCase;
6var sourceSet = new HashSet<string>(new[] { "Alice", "Bob" }, comparer);
7bool containsAll = new[] { "alice" }.All(sourceSet.Contains);
8
9Console.WriteLine(containsAll);

Without the correct comparer, the logic may be right but the result may still look wrong for your domain.

Beware of Deferred Enumeration

IEnumerable<T> is only a promise that the sequence can be enumerated. It might represent:

  • a list in memory
  • a database query
  • a generator
  • a one-time stream

If you call Contains repeatedly on a sequence that is expensive or one-shot, you may end up re-running the sequence or exhausting it unexpectedly. Materializing the source into a HashSet or list is often the safer option for repeated containment checks.

Common Pitfalls

The most common mistake is not deciding whether duplicates matter. Set containment and multiset containment are different problems.

Another pitfall is using required.All(source.Contains) on a large or expensive source sequence without realizing that Contains may rescan the source over and over.

It is also easy to forget equality semantics for custom objects. If Equals or the comparer is wrong, the containment logic can look broken even though the code structure is fine.

Finally, be careful with deferred sequences. Repeated enumeration may be slow, may have side effects, or may not even be valid after the first pass.

Summary

  • First decide whether duplicates matter.
  • For set-style containment, a HashSet plus All is usually the clean and fast answer.
  • 'Except is a concise alternative when set semantics are acceptable.'
  • For multiset containment, compare grouped element counts.
  • Be explicit about equality comparers and sequence materialization when performance or correctness depends on them.

Course illustration
Course illustration

All Rights Reserved.