Python Book · Chapter 8

Dictionaries and sets

Dictionaries and sets are built on hash tables, which makes looking something up almost instant however much data you have. This chapter shows the tools that make them a pleasure to use.

Dictionary comprehensions

Like list comprehensions, but producing key-value pairs:

squares = {n: n * n for n in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

prices = {"apple": 1.2, "melon": 3.5, "fig": 0.4}
cheap = {name: p for name, p in prices.items() if p < 2}
# {'apple': 1.2, 'fig': 0.4}

Counter

Counting things is so common that the standard library has a dictionary built for it:

from collections import Counter

votes = ["red", "blue", "red", "green", "red", "blue"]
counts = Counter(votes)
counts["red"]            # 3
counts["purple"]         # 0, no KeyError
counts.most_common(2)    # [('red', 3), ('blue', 2)]

defaultdict

A defaultdict creates a starting value the first time you use a missing key, which removes a lot of "is it there yet?" checks:

from collections import defaultdict

words = ["apple", "avocado", "banana", "blueberry", "cherry"]
by_letter = defaultdict(list)
for w in words:
    by_letter[w[0]].append(w)

dict(by_letter)
# {'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}

Sets

A set is an unordered collection with no duplicates. Checking x in s is fast, unlike with a list, which has to look at every item.

seen = {"ada", "linus"}
seen.add("grace")
seen.add("ada")          # already there, nothing changes
len(seen)                # 3
"linus" in seen          # True

empty = set()            # {} would be an empty dict

Sets support the operations from maths:

a = {1, 2, 3, 4}
b = {3, 4, 5}
a | b    # union:        {1, 2, 3, 4, 5}
a & b    # intersection: {3, 4}
a - b    # difference:   {1, 2}
a ^ b    # in one only:  {1, 2, 5}

Nested data

Real data is often dictionaries inside lists inside dictionaries. It is the same shape as JSON, which you will meet in the next chapter.

course = {
    "code": "CS101",
    "students": [
        {"name": "Ada", "grade": 91},
        {"name": "Linus", "grade": 78},
    ],
}

for s in course["students"]:
    print(s["name"], s["grade"])

top = max(course["students"], key=lambda s: s["grade"])
top["name"]   # 'Ada'

What can be a key?

Dictionary keys and set items must be hashable, which in practice means they cannot change. Strings, numbers and tuples work. Lists, dictionaries and sets do not:

grid = {(0, 0): "start", (2, 3): "goal"}   # tuple keys: fine
bad = {[0, 0]: "start"}                     # TypeError: unhashable type: 'list'

If a key could change after being stored, the dictionary would look for it in the wrong place, so Python does not allow it.

Exercises

1. Letter frequencies

Using Counter, find the three most common letters in "mississippi".

Show answer
from collections import Counter
Counter("mississippi").most_common(3)
# [('i', 4), ('s', 4), ('p', 2)]

When counts tie, most_common keeps the order in which items were first seen.

2. Shared friends

Ada's friends are Linus, Grace and Alan. Linus's friends are Grace, Ada and Ken. Use sets to find the friends they have in common and the people only Ada knows.

Show answer
ada = {"Linus", "Grace", "Alan"}
linus = {"Grace", "Ada", "Ken"}
ada & linus    # {'Grace'}
ada - linus    # {'Linus', 'Alan'}

Sets have no order, so Python may print these in a different order.

3. Group by length

Group the words in "the quick brown fox jumps over the lazy dog".split() by length, as a dictionary from length to a list of distinct words.

Show answer
from collections import defaultdict

groups = defaultdict(list)
for w in "the quick brown fox jumps over the lazy dog".split():
    if w not in groups[len(w)]:
        groups[len(w)].append(w)

dict(groups)
# {3: ['the', 'fox', 'dog'], 5: ['quick', 'brown', 'jumps'], 4: ['over', 'lazy']}