Skip to main content

How to Check if a List is Contained in Another List in Python

Checking whether one list is "contained" in another has multiple interpretations depending on your requirements:

  1. Subset: Are all elements of B present in A? (Order doesn't matter)
  2. Sublist: Does the exact sequence B appear inside A? (Order matters)
  3. Subset with counts: Does A contain at least as many of each element as B?

This guide covers all three scenarios with efficient solutions.

Subset Check (Order Ignored)

When you only need to verify that elements exist regardless of position or frequency, convert to sets and use the issubset() method.

main_list = [1, 2, 3, 4, 5]
sub_list = [5, 1] # Different order, but elements exist

# issubset() checks if all elements are present
result = set(sub_list).issubset(set(main_list))
print(result) # True

# Alternative syntax using <= operator
result = set(sub_list) <= set(main_list)
print(result) # True
note

Set-based checks ignore duplicates. With this method, [1, 1] is considered a subset of [1] because sets only track unique values.

Performance Characteristics

  • Converting a list to a set: O(n)
  • Subset check: O(m) where m is the smaller set
  • Total: O(n + m)

Sublist Check (Order Matters)

Finding an exact sequence within a larger list requires comparing slices. This approach is similar to finding a substring within a string.

def is_sublist(small, large):
"""Check if 'small' appears as a contiguous sequence in 'large'."""
n = len(small)

# Edge cases
if n == 0:
return True
if n > len(large):
return False

# Slide a window of size n across the large list
for i in range(len(large) - n + 1):
if large[i:i + n] == small:
return True
return False

# Examples
main = [10, 20, 30, 40, 50]

print(is_sublist([20, 30], main)) # True (contiguous match)
print(is_sublist([20, 40], main)) # False (not contiguous)
print(is_sublist([30, 20], main)) # False (wrong order)
print(is_sublist([], main)) # True (empty is always sublist)
tip

For very large lists, consider using the Knuth-Morris-Pratt (KMP) algorithm which reduces time complexity from O(n × m) to O(n + m).

Finding the Sublist Position

To get the index where the sublist starts:

def find_sublist(small, large):
"""Return starting index of sublist, or -1 if not found."""
n = len(small)

if n == 0:
return 0
if n > len(large):
return -1

for i in range(len(large) - n + 1):
if large[i:i + n] == small:
return i
return -1

main = ['a', 'b', 'c', 'd', 'e']
print(find_sublist(['c', 'd'], main)) # Output: 2

Subset with Multiplicity (Counter)

When duplicates matter, use Counter from the collections module. For example, [1, 1] should NOT be a subset of [1, 2],

from collections import Counter

def is_subset_with_counts(small, large):
"""Check if 'large' contains at least as many of each element as 'small'."""
count_small = Counter(small)
count_large = Counter(large)

for element, required_count in count_small.items():
if count_large[element] < required_count:
return False
return True

# Examples
print(is_subset_with_counts([1, 1], [1, 2])) # False (only one 1)
print(is_subset_with_counts([1, 1], [1, 1, 2])) # True (two 1s available)
print(is_subset_with_counts([1, 2], [1, 1, 2])) # True

Using Counter Subtraction

A more concise approach uses Counter subtraction. When you subtract counters, negative counts are automatically removed, leaving only elements where the first counter exceeds the second.

from collections import Counter

def is_subset_with_counts(small, large):
"""Returns True if large contains all elements of small with sufficient counts."""
difference = Counter(small) - Counter(large)
return len(difference) == 0

print(is_subset_with_counts([1, 1, 2], [1, 2, 3])) # False
print(is_subset_with_counts([1, 1, 2], [1, 1, 2, 3])) # True

Checking Multiple Sublists

When searching for any of several possible sublists:

def is_sublist(small, large):
"""Check if 'small' appears as a contiguous sequence in 'large'."""
n = len(small)

# Edge cases
if n == 0:
return True
if n > len(large):
return False

# Slide a window of size n across the large list
for i in range(len(large) - n + 1):
if large[i:i + n] == small:
return True
return False

def contains_any_sublist(candidates, large):
"""Check if any candidate list appears as a sublist in large."""
for candidate in candidates:
if is_sublist(candidate, large):
return True
return False

main = [1, 2, 3, 4, 5]
patterns = [[7, 8], [3, 4], [9, 10]]

print(contains_any_sublist(patterns, main)) # True ([3, 4] matches)

Common Pitfalls

Avoid Using in Operator Incorrectly

The in operator checks if an element exists in a list, not if a list is contained within another list.

main = [1, 2, 3, 4, 5]
sub = [2, 3]

# This checks if the LIST [2, 3] is an ELEMENT of main
print(sub in main) # False

# This is what you actually want
print(set(sub) <= set(main)) # True
Nested Lists Behave Differently

When lists contain other lists as elements, the in operator works for exact matches:

nested = [[1, 2], [3, 4], [5, 6]]

print([3, 4] in nested) # True (exact element match)
print([3] in nested) # False (no exact match)

Summary

RequirementMethodTime Complexity
Existence onlyset(B) <= set(A)O(n + m)
Count-awareCounter subtractionO(n + m)
Exact sequenceSliding windowO(n × m)

Recommendation: For most use cases, the set-based subset check provides the best balance of performance and readability. Use Counter when duplicate counts matter, and the sliding window approach when order and contiguity are required.