LESSON 23 · NEXT STEPS25 min

Dictionary and Set Comprehensions

Build key/value mappings and unique-value sets from iterable data with readable comprehensions.

Before you start: Dictionaries · Tuples and Sets · Loops

Video lesson: Dictionary and Set Comprehensions

The preview is stored on this site. YouTube loads only when you press Play. Watch on YouTube

Read the video transcript

Select a timestamp to watch that moment on YouTube.

  1. 00:00

    Curly braces appear in both dictionary and set comprehensions. If you see empty braces, which collection do you expect? And what happens when you call `.add` on it? This is a small difference with practical consequences. A set stores unique values; a dictionary stores a value under each key. Today we will build both from collections using comprehensions, then use the browser task to normalize words into a lookup table. The important clues are the key-value colon in a dictionary comprehension and the single expression in a set comprehension. Empty braces do not show either form, so they deserve a separate check.

  2. 00:40

    Before running the terminal, predict whether the code will add Ada, print a result, or raise an exception. A comprehension is useful when one clear transformation describes the result. If the logic requires many branches or expensive repeated calculations, a normal loop with named intermediate values can communicate it better. Think of a class roster: you might want one unique set of initials or a mapping from each student's name to a score. Those are different questions even if both results use braces in Python. Predict the desired collection before choosing syntax, because the syntax should follow the data you need.

  3. 01:21

    The error tells us that a dictionary has no `.add` method. Printing its type confirms the empty braces created a dict. The code did not fail because Ada is a bad value; it failed because we chose the wrong collection. A dictionary maps keys to values and uses assignment under a key. A set holds unique values and uses `.add`. To create an empty set, write `set()`. Why is this worth seeing in a comprehension lesson? Because two forms with braces differ by a colon. `{word: len(word) for word in words}` makes a dictionary; `{word for word in words}` makes a set. The empty literal `{}` has no colon or element to reveal an intended type, so Python gives it to dictionaries.

  4. 02:06

    Let us compare a mapping built with a loop to one built with a comprehension, and inspect the type of a set comprehension. The aim is to understand the result, not merely memorize punctuation. The exception points to a missing method, which is evidence about the object's type. Instead of changing the value passed to add, inspect the object itself. This debugging habit applies beyond sets: when a method call fails, ask whether the variable holds the type you expected at that exact line. The dictionary comprehension produces three key-value pairs. Read it from the `for` clause: for each word, use that word as the key and its length as the value.

  5. 02:48

    The colon between the expressions is the sign that a mapping is being built. The set comprehension has only one expression before `for`. It takes the first character of each name. Ada and Ana both contribute A, so the set keeps A once; Bo adds B. We use `sorted` only to display a stable order. A set is about unique membership, and you should not assume it preserves the sequence of input names. The printed type names confirm that the two comprehensions produce different collections. Now return to our actual task. We want a dictionary mapping cleaned words to their lengths. Raw user text may have surrounding spaces, inconsistent capitalization, and blank values.

  6. 03:33

    A raw mapping would create unwanted keys. We need normalization and a filter. Duplicate keys behave differently from duplicate set values. A set simply keeps one member. A dictionary keeps one key and its most recently assigned value. If two normalized records should both survive, use a list of records or a key that distinguishes them. A comprehension cannot invent that business rule for you. A raw dictionary would treat space-padded Ada, uppercase BO, lowercase ada, and two spaces as separate keys. That is usually not the lookup table we want. The corrected comprehension strips surrounding spaces, lowercases the key, and measures the stripped word.

  7. 04:17

    The trailing `if word.strip()` filters out an item that becomes empty after cleaning. Ada and ada now normalize to the same key; a dictionary keeps only one value per key. In this example both give length three, so no information is lost. In another application, duplicate normalized keys might have conflicting values. Decide what should win instead of assuming a dictionary can retain two values under one key. Notice that `strip` appears several times. It is inexpensive for these tiny strings, but a complicated or costly normalization deserves a clear loop with a named intermediate value.

  8. 04:57

    The comprehension does not edit the original list, so you can inspect the raw input after producing the cleaned mapping. That is useful when debugging an unexpected key or checking whether the normalization rule was too aggressive. Try evaluating the filter by itself for each input: stripped Ada is nonempty, stripped BO is nonempty, and two spaces become an empty string. Only the first two kinds of values reach the key and value expressions. That order matters when a blank input would otherwise become an unwanted empty key. The website task asks for `word_lengths(words)`. Return a dictionary rather than printing inside the function.

  9. 05:40

    Test the prepared input, an empty list, and a list of only blank strings. All-blank input should produce `{}`, not a set and not a dictionary with an empty-string key. Check that the source list is unchanged. The automatic checker exercises those cases, and the quiz asks you to distinguish the two kinds of comprehension and the empty-set constructor. Use the Try this example buttons to load code into the browser console, then change one word at a time and predict whether it creates a new key or replaces an existing key. The pattern to remember is key-colon-value for a mapping, one expression for a set, and a trailing `if` when an item should be skipped.

  10. 06:22

    As a further test, pass two spellings that normalize to the same key and see that the result has one entry. Then pass a list of only spaces and confirm it has none. These checks explain the mapping's meaning more clearly than merely seeing that the code runs without an exception.

Understand the concept

A dictionary comprehension has the shape {key_expression: value_expression for item in source}. It evaluates a key and a value for each source item, then puts that pair into a new dictionary. If two source items produce the same key, the later value replaces the earlier one. Choose keys deliberately and test for unintended collisions.

A set comprehension has the shape {expression for item in source}. It collects unique values rather than preserving duplicates. The colon is the visible difference from a dictionary comprehension. An empty pair of braces {} is an empty dictionary, not an empty set; use set() when you need an empty set.

Both forms can end with an if filter. Clean a string before deciding whether it should be included. For example, word.strip() removes surrounding spaces; a whitespace-only word becomes empty and should be skipped. A comprehension is readable when the transformation and filter remain simple.

In the practice task, normalize each word to lowercase, ignore blanks, and map it to its length after stripping spaces. Duplicate normalized keys merge. That behavior is useful for a lookup table, but if repeated records need different values, a dictionary is the wrong collection or the key needs more detail.

  • {key: value for ...} creates a dict
  • {value for ...} creates a set
  • Duplicate dict keys keep the latest value
  • set() is an empty set

See it step by step

Read the code, predict the output, then compare it with the result.

01. Map each word to its length

PYTHON
words = ["cat", "python", "go"]
lengths = {word: len(word) for word in words}
print(lengths)
EXPECTED OUTPUT
{'cat': 3, 'python': 6, 'go': 2}

The key comes from each word and the value comes from len(word). The result is a new dictionary.

02. Collect unique first letters

PYTHON
names = ["Ada", "Ana", "Bo"]
initials = {name[0] for name in names}
print(sorted(initials))
EXPECTED OUTPUT
['A', 'B']

There is no colon, so the comprehension builds a set. Ada and Ana both contribute A, which appears once.

03. Filter and normalize keys

PYTHON
words = [" Ada ", "  ", "BO"]
lengths = {word.strip().lower(): len(word.strip()) for word in words if word.strip()}
print(lengths)
EXPECTED OUTPUT
{'ada': 3, 'bo': 2}

The filter skips the blank word; strip and lower produce the final keys and the values measure cleaned text.

A closer look

Follow the reasoning, inspect each result, then try the suggested changes in the console below.

01 / 03

Move a mapping loop into a comprehension

A dictionary maps keys to values. A loop can start with an empty dictionary, compute a value for each source item, and assign it under a key. A dictionary comprehension expresses that same operation as {key: value for item in source}. The colon separates the key expression from the value expression. Read the for clause first, then identify the key and value produced at each iteration. Both versions below produce a new dictionary; neither needs to modify the source list.

A dictionary cannot keep two different values under the same key simultaneously. If a later item generates a key that already exists, the later value replaces the earlier value. That is useful when the latest record wins, but can also hide an accidental collision. Try repeating one input word and then changing its capitalization. Think about whether your chosen key should treat those spellings as equal before you normalize.

PYTHON
words = ["cat", "python", "go"]
with_loop = {}
for word in words:
    with_loop[word] = len(word)
with_comprehension = {word: len(word) for word in words}
print(with_loop)
print(with_comprehension)
EXPECTED OUTPUT
{'cat': 3, 'python': 6, 'go': 2}
{'cat': 3, 'python': 6, 'go': 2}
Follow the reasoning
  1. Each word becomes a key, and its length becomes the value.
  2. The loop and comprehension compute the same mapping.
  3. The source words remain available for another operation.
02 / 03

A set comprehension removes duplicate values

A set comprehension looks similar to a dictionary comprehension but has no colon: {expression for item in source}. The result is a set of distinct values. In the example, Ada and Ana both contribute the initial A, so the set contains A only once. Sets are useful for membership and uniqueness; they do not promise the sequence order you started with. Use sorted only when you want a stable displayed order, as this guide does.

Do not confuse an empty dictionary with an empty set. Python gives {} to the dictionary type because a dictionary literal needs braces and an empty one has no key/value pairs to distinguish it from a set. The constructor set() explicitly creates an empty set. This matters when you later call .add: a dictionary has no such method. Predict the result type before running, then compare the three printed lines.

PYTHON
names = ["Ada", "Ana", "Bo"]
initials = {name[0] for name in names}
print(sorted(initials))
print(type({}).__name__)
print(type(set()).__name__)
EXPECTED OUTPUT
['A', 'B']
dict
set
Follow the reasoning
  1. The two A initials collapse to one set member.
  2. sorted gives a stable list for display, rather than relying on set iteration order.
  3. Empty braces make a dict; set() makes an empty set.
03 / 03

Normalize before creating a key

Real input often has spaces and inconsistent capitalization. In this small mapping, strip removes surrounding spaces, lower normalizes capitalization, and the trailing if skips values that become empty. The expression word.strip() appears more than once. For a short string this is fine, but for costly transformations a normal loop with a named cleaned variable would be easier to read and avoid repeated work. The goal is a clear mapping, not the shortest possible line.

After normalization, Ada and ada become the same key. The second assignment does not produce a duplicate dictionary entry. In this example both values are the same length; in a more complex mapping you should decide which record wins or choose a different key. Check both an empty list and a list containing only blanks: each should return {}. Also check that the original strings remain untouched, since a comprehension creates a new dictionary.

PYTHON
words = [" Ada ", "BO", "ada", "  "]
lengths = {word.strip().lower(): len(word.strip()) for word in words if word.strip()}
print(lengths)
print({word.strip().lower(): len(word.strip()) for word in ["  "] if word.strip()})
print(words[0])
EXPECTED OUTPUT
{'ada': 3, 'bo': 2}
{}
 Ada 
Follow the reasoning
  1. Whitespace-only text fails the trailing filter.
  2. Both forms of Ada normalize to the same key.
  3. The original first item retains its surrounding spaces.

Try it in Python

Edit the example and run it. Python starts in your browser the first time you click Run.

Python console

Ready to run
Need input()? Add one value per line
Ctrl / ⌘ + Enter to run
OUTPUT
Your output appears here.
Your turnComplete word_lengths(words) with a dictionary comprehension. Strip whitespace, lowercase each nonblank word, and use the length of the stripped word as its value. The prepared list should print {'ada': 3, 'bo': 2}. Return {} for an empty or all-blank input; do not change the input list.
Need a hint?

Use {word.strip().lower(): len(word.strip()) for word in words if word.strip()}. The final if removes blank strings before they become keys.

Complete the task and select Check task to verify your code.

    Quick quiz

    Three questions. You can change your answers and try again.

    01.Which expression creates a dictionary?

    02.What does {name[0] for name in ['Ada', 'Ana']} contain?

    03.How do you construct an empty set?

    Typical mistakes

    Everyone meets these errors. See what causes them and how to fix them.

    Using braces without a colon for a mapping

    COMMON MISTAKE
    lengths = {word for word in ['cat', 'go']}
    FIX
    lengths = {word: len(word) for word in ['cat', 'go']}

    What happens: The result is a set of words with no lengths.
    Include key: value before for when the result should be a dictionary.

    Assuming braces make an empty set

    COMMON MISTAKE
    seen = {}
    seen.add('Ada')
    FIX
    seen = set()
    seen.add('Ada')

    What happens: AttributeError: 'dict' object has no attribute 'add'.
    Python reserves empty braces for a dictionary. Use set() for an empty set.

    Browse this lesson’s problem fixes

    Sources and further reading