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:
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:
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:
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:
- '
sourceis[1, 1, 2]' - '
requiredis[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:
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
EqualsandGetHashCode - or a custom
IEqualityComparer<T>
For example:
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
HashSetplusAllis usually the clean and fast answer. - '
Exceptis 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.

