Introduction
Mapping over dictionary values means applying a function to every value in a dictionary while keeping the keys unchanged. This is a fundamental data transformation operation — used for normalizing data, converting types, applying calculations, and formatting output. Python provides several approaches: dictionary comprehensions, map(), and loops.
Method 1: Dictionary Comprehension (Recommended)
The most Pythonic and readable approach:
1prices = {'apple': 1.20, 'banana': 0.50, 'cherry': 2.00}
2
3# Apply 10% discount to all values
4discounted = {k: round(v * 0.9, 2) for k, v in prices.items()}
5print(discounted) # {'apple': 1.08, 'banana': 0.45, 'cherry': 1.8}
6
7# Convert all values to strings
8formatted = {k: f"${v:.2f}" for k, v in prices.items()}
9print(formatted) # {'apple': '$1.20', 'banana': '$0.50', 'cherry': '$2.00'}
10
11# Apply any function
12import math
13rounded_up = {k: math.ceil(v) for k, v in prices.items()}
14print(rounded_up) # {'apple': 2, 'banana': 1, 'cherry': 2}
Method 2: Using a Function
Pass a named function instead of a lambda:
1def double(x):
2 return x * 2
3
4original = {'a': 1, 'b': 2, 'c': 3}
5doubled = {k: double(v) for k, v in original.items()}
6print(doubled) # {'a': 2, 'b': 4, 'c': 6}
Method 3: dict() with map()
Use map() with a transformation function over the items:
1prices = {'apple': 1.20, 'banana': 0.50, 'cherry': 2.00}
2
3# Using map with a lambda
4result = dict(map(lambda item: (item[0], item[1] * 0.9), prices.items()))
5print(result) # {'apple': 1.08, 'banana': 0.45, 'cherry': 1.8}
This is less readable than a comprehension but useful when you already have a function that transforms key-value pairs.
Method 4: In-Place Modification
Modify the dictionary directly without creating a new one:
1scores = {'alice': 85, 'bob': 92, 'carol': 78}
2
3# Add 5 bonus points to everyone
4for key in scores:
5 scores[key] += 5
6
7print(scores) # {'alice': 90, 'bob': 97, 'carol': 83}
8
9# Or with a function
10for key in scores:
11 scores[key] = min(scores[key], 100) # Cap at 100
In-place modification avoids creating a new dictionary, which matters for very large dictionaries.
Method 5: Using collections (valuesview)
For read-only transformations without creating a new dict:
1prices = {'apple': 1.20, 'banana': 0.50, 'cherry': 2.00}
2
3# Generator — no new dict created
4discounted_values = (v * 0.9 for v in prices.values())
5for price in discounted_values:
6 print(f"{price:.2f}")
Mapping with Conditional Logic
1grades = {'alice': 85, 'bob': 42, 'carol': 91, 'dave': 67}
2
3# Pass/fail classification
4result = {k: 'pass' if v >= 60 else 'fail' for k, v in grades.items()}
5print(result) # {'alice': 'pass', 'bob': 'fail', 'carol': 'pass', 'dave': 'pass'}
6
7# Different transformations based on value
8adjusted = {k: v * 1.1 if v < 60 else v for k, v in grades.items()}
9print(adjusted) # {'alice': 85, 'bob': 46.2, 'carol': 91, 'dave': 67}
Mapping Nested Dictionary Values
1users = {
2 'alice': {'age': 30, 'score': 85},
3 'bob': {'age': 25, 'score': 92},
4}
5
6# Map over nested values
7normalized = {
8 name: {k: v / 100 if k == 'score' else v for k, v in data.items()}
9 for name, data in users.items()
10}
11print(normalized)
12# {'alice': {'age': 30, 'score': 0.85}, 'bob': {'age': 25, 'score': 0.92}}
Mapping Keys and Values Together
Sometimes you need to transform both:
1raw = {' Name ': ' Alice ', ' Age': ' 30 ', 'City ': ' NYC '}
2
3# Strip whitespace from both keys and values
4cleaned = {k.strip(): v.strip() for k, v in raw.items()}
5print(cleaned) # {'Name': 'Alice', 'Age': '30', 'City': 'NYC'}
6
7# Lowercase keys, uppercase values
8transformed = {k.lower(): v.upper() for k, v in cleaned.items()}
9print(transformed) # {'name': 'ALICE', 'age': '30', 'city': 'NYC'}
Mapping with External Data
1# Apply a lookup table
2grade_points = {'A': 4.0, 'B': 3.0, 'C': 2.0, 'D': 1.0, 'F': 0.0}
3student_grades = {'alice': 'A', 'bob': 'C', 'carol': 'B'}
4
5gpa = {name: grade_points[grade] for name, grade in student_grades.items()}
6print(gpa) # {'alice': 4.0, 'bob': 2.0, 'carol': 3.0}
1import timeit
2
3d = {i: i for i in range(10000)}
4
5# Dict comprehension — fastest
6timeit.timeit('{k: v * 2 for k, v in d.items()}', globals={'d': d}, number=1000)
7# ~2.5ms
8
9# map + dict — slightly slower
10timeit.timeit('dict(map(lambda x: (x[0], x[1] * 2), d.items()))', globals={'d': d}, number=1000)
11# ~4.0ms
12
13# For loop — slowest
14timeit.timeit('''
15new = {}
16for k, v in d.items():
17 new[k] = v * 2
18''', globals={'d': d}, number=1000)
19# ~3.5ms
Dictionary comprehension is consistently the fastest approach.
Common Pitfalls
Modifying dict during iteration: for k in d: d[k] = ... is safe (modifying values only), but for k in d: del d[k] raises RuntimeError. Create a new dict or iterate over a copy of keys: for k in list(d).
Side effects in comprehension: Dictionary comprehensions should be pure transformations. Avoid putting print(), database calls, or other side effects inside comprehensions — use a loop instead.
Forgetting .items(): {k: v*2 for k, v in d} fails because iterating over a dict yields keys only. Use .items() to get key-value pairs.
Type errors: If values have mixed types, the transformation function may fail on some values. Use try/except or check types: {k: int(v) if isinstance(v, str) else v for k, v in d.items()}.
Large dictionaries: Comprehensions create a new dictionary in memory. For very large dicts where you only need to iterate once, use a generator expression instead of materializing the result.
Summary
Use {k: f(v) for k, v in d.items()} for the fastest, most readable value transformation
Use dict(map(lambda item: (item[0], f(item[1])), d.items())) if you already have a transformation function
Modify in place with for k in d: d[k] = f(d[k]) to avoid creating a new dictionary
Add if conditions for conditional mapping: {k: f(v) for k, v in d.items() if condition(v)}
Dictionary comprehension is faster than both map() and explicit loops