Dictionary Comprehensions

The same syntax as a list comprehension, with a key and a value separated by a colon. It is the fastest way to build, filter, invert or reshape a mapping.

The shape

{ key_expression : value_expression   for item in iterable   if condition }
# The loop
squares = {}
for n in range(5):
    squares[n] = n * n

# The comprehension
squares = {n: n * n for n in range(5)}

print(squares)      # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
The colon is the only difference from a set comprehension. {n for n in x} is a set; {n: n for n in x} is a dictionary.

Building from two sequences

names = ["Meera", "Arun", "Sara"]
scores = [92, 78, 85]

result = {name: score for name, score in zip(names, scores)}
print(result)              # {'Meera': 92, 'Arun': 78, 'Sara': 85}

print(dict(zip(names, scores)))     # the same thing, shorter

# With a transformation, where dict(zip(...)) no longer suffices
print({name.lower(): score for name, score in zip(names, scores)})

Building from a single sequence

words = ["apple", "banana", "fig"]

print({w: len(w) for w in words})            # {'apple': 5, 'banana': 6, 'fig': 3}
print({w: w.upper() for w in words})
print({w[0]: w for w in words})              # first letter to word; later wins
print({i: w for i, w in enumerate(words)})   # index to word

Filtering

scores = {"Meera": 92, "Arun": 45, "Sara": 78, "Ravi": 30}

passed = {name: score for name, score in scores.items() if score >= 50}
print(passed)                    # {'Meera': 92, 'Sara': 78}

short_names = {k: v for k, v in scores.items() if len(k) <= 4}
print(short_names)               # {'Arun': 45, 'Sara': 78, 'Ravi': 30}

# Filter and transform at once
print({k.upper(): v + 5 for k, v in scores.items() if v < 50})

Transforming values

prices = {"pen": 10.5, "book": 250.0, "bag": 899.99}

with_tax = {item: round(price * 1.18, 2) for item, price in prices.items()}
print(with_tax)

as_text = {item: f"Rs {price:,.2f}" for item, price in prices.items()}
print(as_text)

# Clamp every value into a range
clamped = {k: max(0, min(100, v)) for k, v in {"a": 150, "b": -20}.items()}
print(clamped)                   # {'a': 100, 'b': 0}

Transforming keys

raw = {"  Name ": "Meera", "AGE": 27, "City ": "Pune"}

cleaned = {key.strip().lower(): value for key, value in raw.items()}
print(cleaned)                   # {'name': 'Meera', 'age': 27, 'city': 'Pune'}

Normalising keys as data comes in is one of the most useful things a dictionary comprehension does, and it is worth doing at the boundary of your program.

Inverting

ages = {"Meera": 27, "Arun": 31}

inverted = {age: name for name, age in ages.items()}
print(inverted)                  # {27: 'Meera', 31: 'Arun'}
ages = {"Meera": 27, "Arun": 31, "Sara": 27}
print({age: name for name, age in ages.items()})     # {27: 'Sara', 31: 'Arun'}

Duplicate values collapse, and the last one wins. When that matters, group instead of inverting:

from collections import defaultdict

grouped = defaultdict(list)
for name, age in ages.items():
    grouped[age].append(name)
print(dict(grouped))             # {27: ['Meera', 'Sara'], 31: ['Arun']}

Conditional values

scores = {"Meera": 92, "Arun": 45}

labels = {name: ("pass" if score >= 50 else "fail") for name, score in scores.items()}
print(labels)                    # {'Meera': 'pass', 'Arun': 'fail'}

grades = {
    name: "A" if s >= 90 else "B" if s >= 75 else "C"
    for name, s in {"a": 95, "b": 80, "c": 60}.items()
}
print(grades)                    # {'a': 'A', 'b': 'B', 'c': 'C'}

The second example works, but a chain of conditional expressions is hard to read. A small helper function called from the comprehension is usually better.

Nested dictionary comprehensions

table = {
    row: {col: row * col for col in range(1, 4)}
    for row in range(1, 4)
}
print(table)
# {1: {1: 1, 2: 2, 3: 3}, 2: {1: 2, 2: 4, 3: 6}, 3: {1: 3, 2: 6, 3: 9}}

print(table[2][3])               # 6
people = {
    "meera": {"age": 27, "city": "Pune", "temp": 1},
    "arun": {"age": 31, "city": "Kochi", "temp": 2},
}

# Drop a field from every nested record
cleaned = {
    name: {k: v for k, v in details.items() if k != "temp"}
    for name, details in people.items()
}
print(cleaned)

Practical examples

Counting without Counter

text = "mississippi"
counts = {ch: text.count(ch) for ch in set(text)}
print(counts)                    # {'m': 1, 'i': 4, 's': 4, 'p': 2}

This is correct but does more work than it looks: count scans the whole string once per distinct character. For a long text, Counter is the right tool.

Building a lookup index

records = [
    {"id": 101, "name": "Meera"},
    {"id": 102, "name": "Arun"},
]

by_id = {r["id"]: r for r in records}
print(by_id[102]["name"])        # Arun

Turning a list of records into a dictionary keyed by id changes lookup from a scan into a direct access. It is one of the highest value comprehensions you will write.

Applying defaults

defaults = {"theme": "light", "size": 12, "wrap": True}
user = {"size": 14}

settings = {key: user.get(key, value) for key, value in defaults.items()}
print(settings)                  # {'theme': 'light', 'size': 14, 'wrap': True}

Selecting a subset of keys

record = {"name": "Meera", "age": 27, "password": "secret", "token": "abc"}
public_fields = {"name", "age"}

safe = {k: v for k, v in record.items() if k in public_fields}
print(safe)                      # {'name': 'Meera', 'age': 27}

Common mistakes

  • Forgetting the colon and building a set instead.
  • Iterating a dictionary directly and getting only keys; use .items().
  • Inverting a dictionary with duplicate values and silently losing entries.
  • Producing duplicate keys from a transformation, so earlier entries disappear.
  • Nesting conditional expressions until the line is unreadable.
  • Using {k: expensive(k) for k in items} where expensive rescans the data each time.

Best practices

  • Use .items() whenever both key and value are needed.
  • Normalise keys as data enters your program.
  • Build an id keyed index once instead of scanning a list repeatedly.
  • Call a named helper from the comprehension rather than embedding a conditional chain.
  • Group with defaultdict when inverting could collide.

Practice

  1. Build a dictionary mapping each word in a sentence to its length, excluding words under four characters.
  2. Invert a dictionary safely so that duplicate values collect a list of keys.
  3. Turn a list of product records into a dictionary keyed by product code.
  4. Apply user overrides on top of defaults without losing any default key.
  5. Build a nested multiplication table from 1 to 5 and look up a single cell.

Conclusion

A dictionary comprehension is the shortest path between a sequence and a mapping. Use it to build indexes, normalise keys, filter records and apply defaults - and reach for defaultdict the moment two items could produce the same key.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Python notes →
Python

Trees and Graphs

A tree is a graph with no cycles and one root. Both are walked with the same two strategies - depth first with a stack, breadth first with a queue.

Read more
Python

Sorting Algorithms

Python sorts for you in n log n. Implementing bubble, insertion, merge and quick sort is still worth doing, because it teaches how algorithms are comp...

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.