Skip to content

How to Compare Lists of Different Lengths in Python

How to compare Python lists with different lengths using zip(), zip_longest(), sets, and Counter, with examples for comparing elements and finding content differences.

When comparing two lists of unequal size, the behavior you need depends on your goal: stop when the shortest list ends, process all elements with placeholders, or find differences regardless of order.

This guide covers each approach with practical examples and performance considerations.

Stop at Shortest List (Standard zip)

Use the built-in zip() when you only care about indices present in both lists. Extra elements in the longer list are silently ignored:

list_a = [10, 20, 30, 40, 50]
list_b = [10, 25, 30]  # Shorter

for i, (val_a, val_b) in enumerate(zip(list_a, list_b)):
    if val_a == val_b:
        print(f"Index {i}: Match ({val_a})")
    else:
        print(f"Index {i}: Mismatch ({val_a} vs {val_b})")

Output:

Index 0: Match (10)
Index 1: Mismatch (20 vs 25)
Index 2: Match (30)

Compare All Elements (zip_longest)

When every element matters, use itertools.zip_longest. It continues until the longest list is exhausted, filling gaps with a placeholder value:

from itertools import zip_longest

expected = ['header', 'body', 'footer', 'sidebar']
received = ['header', 'body']

for exp, rec in zip_longest(expected, received, fillvalue="<MISSING>"):
    if exp == rec:
        print(f"✓ {exp}")
    else:
        print(f"✗ Expected '{exp}', got '{rec}'")

Output:

✓ header
✓ body
✗ Expected 'footer', got '<MISSING>'
✗ Expected 'sidebar', got '<MISSING>'

Detecting Which List is Shorter

from itertools import zip_longest

def compare_with_source(list_a, list_b):
    """Compare lists and identify which has missing elements."""
    MISSING = object()  # Unique sentinel value
    
    for i, (a, b) in enumerate(zip_longest(list_a, list_b, fillvalue=MISSING)):
        if a is MISSING:
            print(f"Index {i}: Only in list_b: {b}")
        elif b is MISSING:
            print(f"Index {i}: Only in list_a: {a}")
        elif a != b:
            print(f"Index {i}: Different: {a} vs {b}")

list_a = [1, 2, 3, 4]
list_b = [1, 2, 5]

compare_with_source(list_a, list_b)

Output:

Index 2: Different: 3 vs 5
Index 3: Only in list_a: 4

Find Differences by Content (Sets)

When order doesn't matter and you need to find what's missing or extra, sets provide O(1) lookup performance:

required_fields = ['id', 'name', 'email', 'phone']
submitted_fields = ['id', 'name', 'address']

required = set(required_fields)
submitted = set(submitted_fields)

missing = required - submitted
extra = submitted - required
common = required & submitted

print(f"Missing: {missing}")   # {'email', 'phone'}
print(f"Extra: {extra}")       # {'address'}
print(f"Common: {common}")     # {'id', 'name'}

Symmetric Difference

Find all elements that are in either list but not both:

list_a = [1, 2, 3, 4]
list_b = [3, 4, 5, 6]

unique = set(list_a) ^ set(list_b)
print(f"Unique to either: {unique}")  # {1, 2, 5, 6}

Comparing with Element Counts (Counter)

When duplicates matter, use Counter to track occurrences:

from collections import Counter

inventory_expected = ['apple', 'apple', 'banana', 'orange']
inventory_actual = ['apple', 'banana', 'banana', 'grape']

expected = Counter(inventory_expected)
actual = Counter(inventory_actual)

missing = expected - actual  # Items we're short on
extra = actual - expected    # Items we have too many of

print(f"Missing: {dict(missing)}")  # Missing: {'apple': 1, 'orange': 1}
print(f"Extra: {dict(extra)}")      # Extra: {'banana': 1, 'grape': 1}

Practical Example: Data Validation

A complete example for validating API responses:

from itertools import zip_longest

def validate_response(expected_fields, actual_data):
    """Validate API response has required fields in order."""
    errors = []
    MISSING = object()
    
    for i, (expected, actual) in enumerate(
        zip_longest(expected_fields, actual_data.keys(), fillvalue=MISSING)
    ):
        if expected is MISSING:
            errors.append(f"Unexpected field at position {i}: '{actual}'")
        elif actual is MISSING:
            errors.append(f"Missing required field: '{expected}'")
        elif expected != actual:
            errors.append(f"Field order mismatch at {i}: expected '{expected}', got '{actual}'")
    
    return errors

expected = ['id', 'name', 'email', 'created_at']
response = {'id': 1, 'name': 'Alice', 'status': 'active'}

errors = validate_response(expected, response)
for error in errors:
    print(f"  ✗ {error}")

Output:

  ✗ Field order mismatch at 2: expected 'email', got 'status'
  ✗ Missing required field: 'created_at'

Performance Comparison

import timeit
from itertools import zip_longest

list_a = list(range(10000))
list_b = list(range(8000))

zip_time = timeit.timeit(
    lambda: list(zip(list_a, list_b)),
    number=1000
)

zip_longest_time = timeit.timeit(
    lambda: list(zip_longest(list_a, list_b)),
    number=1000
)

set_time = timeit.timeit(
    lambda: set(list_a) - set(list_b),
    number=1000
)

print(f"zip: {zip_time:.4f}s")
print(f"zip_longest: {zip_longest_time:.4f}s")
print(f"set difference: {set_time:.4f}s")

Output:

zip: 0.4418s
zip_longest: 0.5691s
set difference: 1.1501s

Summary

Method Handles Length Diff Preserves Order Best For
zip() Truncates to shortest Yes Parallel iteration when extras don't matter
zip_longest() Fills with placeholder Yes Row-by-row comparison of tabular data
set operations Compares all elements No Finding missing/extra items quickly
Counter Compares with counts No When duplicates matter
Navigation