1.2.35 · D4Introduction to Programming (Python)

Exercises — Dictionary and set comprehensions

2,221 words10 min readBack to topic

Before you start, here is the one mental picture we keep reusing — the anatomy of a comprehension. Each coloured zone is a job: what you store, where the source comes in, and what you keep.

Figure — Dictionary and set comprehensions

Level 1 — Recognition

Goal: read a comprehension and predict its exact output. No writing yet — just seeing.

L1.1

What is the type and value of each?

A = {}
B = {1, 2, 3}
C = {"a": 1}
D = {x for x in range(3)}
Recall Solution
  • Aempty dict (braces alone = dict, the classic trap). type(A) is dict.
  • B → a set {1, 2, 3} (values, no colon).
  • C → a dict {"a": 1} (colon present ⇒ key:value).
  • D → a set comprehension producing {0, 1, 2}.

Rule used: colon anywhere in the braces ⇒ dict; otherwise a set; empty braces ⇒ dict.

L1.2

Predict the output:

{n: n + 10 for n in range(3)}
Recall Solution

The DRIVE gives n = 0, 1, 2. The HEAD stores n: n+10. Result: {0: 10, 1: 11, 2: 12}.

L1.3

Predict the output:

{len(s) for s in ["hi", "yo", "sun"]}
Recall Solution

Lengths are 2, 2, 3. No colon ⇒ set, and a set drops duplicates. The two 2s collapse to one. Result: {2, 3}.


Level 2 — Application

Goal: write a comprehension from scratch for a stated task.

L2.1

Build a dict mapping each number 1..5 to its cube.

Recall Solution
{n: n**3 for n in range(1, 6)}
# {1: 1, 2: 8, 3: 27, 4: 64, 5: 125}

HEAD n: n**3, DRIVE for n in range(1, 6). range(1, 6) stops before 6, so it is 1..5.

L2.2

From nums = [4, 7, 10, 13], build a set of only the numbers divisible by 2.

Recall Solution
nums = [4, 7, 10, 13]
{x for x in nums if x % 2 == 0}
# {10, 4}

The GATE if x % 2 == 0 runs before the element is kept. Only 4 and 10 pass. (x % 2 is the remainder after dividing by 2; == 0 means "evenly divisible".)

L2.3

Build a dict mapping each word in ["cat", "dog", "ox"] to its length.

Recall Solution
words = ["cat", "dog", "ox"]
{w: len(w) for w in words}
# {'cat': 3, 'dog': 3, 'ox': 2}

Note: values may repeat freely (two 3s here). Only keys must be unique — and the words already are.


Level 3 — Analysis

Goal: reason about collisions, ordering, and what silently disappears.

L3.1

How many entries does this dict have, and which survive?

{x % 3: x for x in range(7)}
Recall Solution

x runs 0..6. The key is x % 3, so keys cycle 0,1,2,0,1,2,0. Same key ⇒ later value overwrites earlier. Track each key's last writer:

  • key 0: written by x=0,3,6 → last is 6
  • key 1: written by x=1,4 → last is 4
  • key 2: written by x=2,5 → last is 5

Result: {0: 6, 1: 4, 2: 5}3 entries, not 7.

L3.2

What is the size of this set?

{abs(x) for x in [-2, -1, 0, 1, 2]}
Recall Solution

abs gives 2, 1, 0, 1, 2. Distinct values: {0, 1, 2}size 3. Both -1,1 collapse to 1 and both -2,2 collapse to 2. That halving is the whole point of a set: it guarantees no duplicates.

L3.3

Invert d = {"a": 1, "b": 2, "c": 1} with {v: k for k, v in d.items()}. How many entries result?

Recall Solution

.items() yields ("a",1), ("b",2), ("c",1). New keys are the old values 1, 2, 1. Key 1 appears twice (from "a" then "c"); the later wins → "c". Result: {1: "c", 2: "b"}2 entries (one grade collided).


Level 4 — Synthesis

Goal: combine comprehensions with ternaries, nesting, and other iterables.

L4.1

Map each n in 0..5 to the string "even" or "odd".

Recall Solution
{n: ("even" if n % 2 == 0 else "odd") for n in range(6)}
# {0:'even',1:'odd',2:'even',3:'odd',4:'even',5:'odd'}

The Ternary conditional expression A if cond else B lives in the value slot (head). It chooses, so every n still gets an entry — nothing is filtered out.

L4.2

From two lists names = ["Asha", "Ravi"] and scores = [90, 75], build the pairing dict.

Recall Solution
names = ["Asha", "Ravi"]
scores = [90, 75]
{name: score for name, score in zip(names, scores)}
# {'Asha': 90, 'Ravi': 75}

zip walks both lists in lockstep, handing out ("Asha", 90) then ("Ravi", 75). We unpack each pair into name, score.

L4.3

Build a set of all distinct letters appearing across ["bee", "add", "cab"].

Recall Solution
words = ["bee", "add", "cab"]
{ch for w in words for ch in w}
# {'b', 'e', 'a', 'd', 'c'}

Nested drive: read the two fors left to right, outer to inner — "for each word w, for each character ch in that word." Letters b,e,e,a,d,d,c,a,b collapse to the 5 distinct {a, b, c, d, e}.


Level 5 — Mastery

Goal: design a small tool, choosing dict vs set and handling collisions deliberately.

L5.1

Given data = [3, 1, 4, 1, 5, 9, 2, 6, 5], build a dict mapping each distinct value to the count of how many times it appears. (Hint: build the distinct set first, then count.)

Recall Solution
data = [3, 1, 4, 1, 5, 9, 2, 6, 5]
{v: data.count(v) for v in set(data)}
# {1: 2, 2: 1, 3: 1, 4: 1, 5: 2, 6: 1, 9: 1}

Why set(data) in the DRIVE? It gives each value once, so no wasted recomputation and no collisions. data.count(v) scans the list counting v. 1 and 5 appear twice; everything else once. Total distinct keys = 7.

L5.2

From pairs = [("a", 1), ("b", 2), ("a", 3)], build a dict where each key keeps ALL its values in a list (so the collision on "a" is preserved, not overwritten).

Recall Solution

A single comprehension would let "a": 3 overwrite "a": 1. To keep all, group inside the value using the distinct keys as the drive:

pairs = [("a", 1), ("b", 2), ("a", 3)]
keys = {k for k, _ in pairs}                 # {'a', 'b'}
{k: [v for kk, v in pairs if kk == k] for k in keys}
# {'a': [1, 3], 'b': [2]}

The outer dict comprehension drives over distinct keys; the inner list comprehension gathers every v whose original key matched. "a" now safely holds [1, 3].

L5.3

Build a dict mapping each n in 1..10 to "fizz" if divisible by 3, "buzz" if divisible by 5, "fizzbuzz" if by both, else n itself.

Recall Solution
{n: ("fizzbuzz" if n % 15 == 0
     else "fizz" if n % 3 == 0
     else "buzz" if n % 5 == 0
     else n)
 for n in range(1, 11)}
# {1:1, 2:2, 3:'fizz', 4:4, 5:'buzz', 6:'fizz',
#  7:7, 8:8, 9:'fizz', 10:'buzz'}

Order matters: test n % 15 (both) first, otherwise a fizzbuzz number would be caught by the n % 3 branch and mislabelled. This is a chained ternary living entirely in the value slot — still no filtering, every n gets an entry.



Connections

  • Dictionary and set comprehensions — parent topic these exercises drill
  • List comprehensions — the inner [v for ...] used in L5.2
  • Ternary conditional expression — the value-chooser in L4.1 and L5.3
  • Dictionaries in Python · Sets in Python — the structures being built
  • Hashing and hashable types — why keys must be unique & hashable
  • Generator expressions — lazy cousin using (...)