Backend / Python core / Tricky questions / 42_duplicate_keys_in_comprehension.md

Duplicate keys in dict comprehensions

Updated 3 min read source
On this page7
  1. The gotcha
  2. Minimal repro
  3. Why it happens
  4. When this matters
  5. What about set comprehensions with duplicate keys?
  6. “Last wins” can be the feature
  7. Interview angle

Duplicate keys in dict comprehensions

The gotcha

{f(x): x for x in iter} silently keeps only the last value when f(x) collides. No warning, no error. Different from list/set comprehensions, where everything is preserved.

Minimal repro

python
d = {x % 3: x for x in range(10)}
print(d)        # {0: 9, 1: 7, 2: 8}
x x%3 written
0 0 d[0] = 0
1 1 d[1] = 1
2 2 d[2] = 2
3 0 d[0] = 3 (overwrites)
4 1 d[1] = 4 (overwrites)
5 2 d[2] = 5 (overwrites)
6 0 d[0] = 6 (overwrites)
7 1 d[1] = 7 (overwrites)
8 2 d[2] = 8 (overwrites)
9 0 d[0] = 9 (overwrites)

The earlier values for x=0..8 are silently dropped.

Why it happens

{k: v for ...} is sugar for:

python
result = {}
for ... :
    result[k] = v
return result

Each result[k] = v overwrites without check. Standard dict assignment semantics.

By contrast, list comprehensions keep duplicates:

python
# [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]
[x % 3 for x in range(10)]

And set comprehensions dedupe but the values are the keys:

python
{x % 3 for x in range(10)}   # {0, 1, 2}

When this matters

Building a lookup from records:

python
users = [{"id": 1, "name": "Alice"}, {"id": 1, "name": "Bob"}]
by_id = {u["id"]: u for u in users}
# {1: {"id": 1, "name": "Bob"}}   ← Alice silently lost

If duplicates indicate a data bug, surface it:

python
def index_by(items, key):
    result = {}
    for item in items:
        k = key(item)
        if k in result:
            raise ValueError(f"duplicate key: {k}")
        result[k] = item
    return result

Or aggregate intentionally:

python
from collections import defaultdict
by_id = defaultdict(list)
for u in users:
    by_id[u["id"]].append(u)
# {1: [{"id": 1, "name": "Alice"}, {"id": 1, "name": "Bob"}]}

What about set comprehensions with duplicate keys?

set and dict.keys() dedupe by hash + eq. So if you want unique keys in a single pass:

python
ids = {u["id"] for u in users}      # {1}

But you don’t get to pick which item wins for each id — for that, you need a dict comprehension or explicit loop.

“Last wins” can be the feature

When the input is sorted such that the last value is the canonical one (latest update, highest priority), this behavior is what you want:

python
events = sorted(events, key=lambda e: e.timestamp)
# last (newest) wins
latest_per_user = {e.user_id: e for e in events}

For “first wins”:

python
events = sorted(events, key=lambda e: e.timestamp, reverse=True)
# first (newest, after reverse) wins
first_per_user = {e.user_id: e for e in events}

Or:

python
first = {}
for e in events:
    if e.user_id not in first:
        first[e.user_id] = e

Interview angle 4

  • Q: “What does {x % 3: x for x in range(10)} produce?” — {0: 9, 1: 7, 2: 8}. Last value per key wins.
  • Q: “How would you keep all values per key instead?” — defaultdict(list) and append.
  • Follow-up: “How would you detect duplicate keys at build time?” — explicit loop with if k in result: raise.
  • Follow-up: “Difference from list comprehension?” — list keeps duplicates, dict overwrites.

See collections — specialized containers, Inverting a dict — what could go wrong.