Programming Fundamentals

Dictionaries and Nested Structures

↩ Back
01

Dictionaries

Reference
Building a Dictionary

A dictionary stores pairs written as key: value inside braces, separated by commas. The key names the entry and the value holds the data. Any type can be a value, and one dictionary may mix several types.

student = {"name": "Ana", "age": 21, "passed": True} stock = {"pen": 4, "book": 2} empty = {} print(len(student)) # 3 print(student["name"]) # Ana
"name""Ana"
"age"21
"passed"True
What a dictionary promises
Unique keysA key appears once. Writing to a key that is already there replaces its value rather than creating a second entry.
Fixed keysA key must be of a type that cannot change: a string, an integer number, a float or a tuple. A list cannot be a key.
Free valuesA value has no restriction. It may be a number, a string, a list, or another dictionary.
OrderEntries stay in the order you wrote them, and that order is what a loop follows.
len(d) reports how many pairs the dictionary holds
Reference
Reading a Value

Square brackets hold the key instead of a position. There is no index, no slice and no negative counting: the key is the only way in.

stock = {"pen": 4, "book": 2, "bag": 7}
Every form you need
FormMeaningExampleResult
d[k]the value stored under kstock["pen"]4
d.get(k)the same value, None when k is absentstock.get("cap")None
d.get(k, v)the value, or v when k is absentstock.get("cap", 0)0
k in dtrue when k is one of the keys"book" in stockTrue
len(d)how many pairslen(stock)3
d.keys()every keystock.keys()['pen', 'book', 'bag']
d.values()every valuestock.values()[4, 2, 7]
d.items()every pairstock.items()[('pen', 4), ...]
Brackets vs get, on a missing key

A key that does not exist raises KeyError and stops the program. get raises nothing: it answers None, or the default you pass as a second argument. Use brackets when the key must be there, and get when it may be missing.

Interactive
Key Lookup

The dictionary below holds three keys. Pick one, then read it with brackets and with get. The fourth option in the list is a key that was never written.

Try it
student = {"name": "Ana", "age": 21, "passed": True}
Output
pick a key and press a button
Choose "city" and press both buttons to see the difference.
Reference
Changing a Dictionary

A dictionary is mutable. One assignment covers two jobs: it creates the pair when the key is new, and it replaces the value when the key is already there. Each row below starts from the same dictionary on the left.

stock["bag"] = 7the key is new, so a pair is added at the end
Before
"pen": 4
"book": 2
After
"pen": 4
"book": 2
"bag": 7
stock["pen"] = 9the key exists, so the value is replaced in place
Before
"pen": 4
"book": 2
After
"pen": 9
"book": 2
del stock["pen"]drops the pair, raises KeyError when the key is absent
Before
"pen": 4
"book": 2
After
"book": 2
taken = stock.pop("pen")drops the pair and returns the value taken = 4
Before
"pen": 4
"book": 2
After
"book": 2
stock.update({"book": 5, "bag": 7})writes several pairs at once, replacing what matches
Before
"pen": 4
"book": 2
After
"pen": 4
"book": 5
"bag": 7
"book" in stocktrue when the key is present, false otherwise
Before
"pen": 4
"book": 2
After
True

Reassign or not
CallWhat it returnsWrite it as
d[k] = v, del d[k], .update, .clearNonestock.update(extra)
.popthe value it removedtaken = stock.pop("pen")
.get, k in d, len(d)a new value, d stays as it wascount = stock.get("cap", 0)
A dictionary has no append and no insert. Assignment to a new key is how it grows.
02

Walking a Dictionary

Detail
Keys, Values and Items

A plain for loop over a dictionary hands you the keys, one per pass, never the values. When you want the values as well, ask for items and receive both names in the same header.

Looping over the keys
Looping over the pairs
stock = {"pen": 4, "book": 2}for key in stock: print(key, stock[key])
stock = {"pen": 4, "book": 2}for key, value in stock.items(): print(key, value)
Output
press run to execute
Output
press run to execute
for key in d · for value in d.values() · for key, value in d.items()

The second header uses two names because every item is a tuple of length two. That is the unpacking you already met with tuples, applied once per pass.

Detail
Counting With a Dictionary

Counting is the task dictionaries were made for. The pattern starts from an empty dictionary and uses get with a default of zero, so the first sighting of a letter needs no special treatment.

letters = ["a", "b", "a"]counts = {}for ch in letters: counts[ch] = counts.get(ch, 0) + 1print(counts)
Output
press run to execute
What the dictionary holds on each pass
Passchcounts.get(ch, 0)counts after the line
1'a'0{'a': 1}
2'b'0{'a': 1, 'b': 1}
3'a'1{'a': 2, 'b': 1}
Without the default, the first pass would raise KeyError, since 'a' is not a key yet.
03

Matrices

Reference
Building a Matrix

A matrix is a list whose values are themselves lists. Each inner list is one row, and the values inside it are the columns of that row. Nothing new is added to the language: it is the list you already know, holding lists.

grid = [[8, 3, 5], [1, 9, 4], [6, 2, 7]] print(len(grid)) # 3 rows print(len(grid[0])) # 3 columns print(grid[1]) # [1, 9, 4] print(grid[1][2]) # 4
FormMeaningExampleResult
grid[r]the whole row at position rgrid[0][8, 3, 5]
grid[r][c]one value: row r, column cgrid[2][0]6
len(grid)how many rowslen(grid)3
len(grid[r])how many columns in that rowlen(grid[1])3
grid[r][c] = vreplaces one valuegrid[0][1] = 0row becomes [8, 0, 5]
Reading the brackets in order

The first bracket applies to the matrix and gives a list. The second bracket applies to that list and gives a value. So grid[1][2] means: take row 1, then take position 2 of what you got. Row first, column second.

Interactive
Grid Explorer

Click any cell of the matrix. The panel below shows the expression that reaches it, the value it holds, and the row that contains it.

Try it
[0][1][2]
[0]
[1]
[2]
Expressiongrid[r][c] Value? grid[r]?
Click a cell to read it.
Detail
Nested Loops

One loop walks the rows. A second loop, written inside the first, walks the values of the row that the outer loop is holding at that moment. The inner loop runs from start to finish once per row.

grid = [[1, 2], [3, 4]]total = 0for row in grid: for value in row: total = total + valueprint(total)
watch the inner loop restart on every row
Output
press run to execute

When the positions matter

Walking the values is enough for a sum. When the output has to name where each value sits, count with range on both levels instead.

for r in range(len(grid)): for c in range(len(grid[r])): print(r, c, grid[r][c])
The inner header reads len(grid[r]), not len(grid), because it counts columns of the current row.
04

Deeper Nesting

Reference
A List of Records

A value inside a dictionary may be a list, and a value inside a list may be a dictionary. The two types nest in any combination, and each level is read with the bracket that belongs to it.

Three shapes you will meet
ShapeWritten asReach one value withUseful for
Dictionary of lists{"ana": [7, 9]}marks["ana"][0]several results under one name
List of dictionaries[{"name": "Ana"}]people[0]["name"]a table of records
Dictionary of dictionaries{"ana": {"age": 21}}users["ana"]["age"]records reached by name
people = [{"name": "Ana", "age": 21}, {"name": "Bo", "age": 30}]for person in people: print(person["name"])
Output
press run to execute

The loop hands you one dictionary per pass, and that dictionary is read with a key, as any other. A matrix is the same idea with lists on the inside.

Detail
Reading a Path

A chain of brackets looks long, and it is read as a chain of small readings. Each bracket applies to the result of the one before it, so the type of that result decides what the next bracket may hold.

school = { "ana": {"marks": [7, 9, 5], "year": 2}, "bo": {"marks": [6, 8], "year": 1} } print(school["ana"]["marks"][1]) # 9
1
school is a dictionary, so the first bracket holds a key. school["ana"] gives the inner dictionary.
2
That result is a dictionary too, so the second bracket holds a key. ["marks"] gives the list [7, 9, 5].
3
That result is a list, so the third bracket holds a position. [1] gives 9.

Writing into a nested structure
school["ana"]["marks"].append(10) # [7, 9, 5, 10] school["bo"]["year"] = 2 # replaces 1 with 2 school["cy"] = {"marks": [], "year": 1} # a new record

The path on the left reaches the object you want, and the last part of the line acts on it. Reaching a list lets you call list methods, reaching a dictionary lets you assign a key.

A wrong key stops the program at the bracket that used it, so read the error from left to right along the path.
Comparison
List or Dictionary
Questionlistdictionary
Written with[10, 20, 30]{"a": 10, "b": 20}
Reached bya position, fixed by the ordera key, chosen by you
SlicingYesNo
RepeatsThe same value may appear many timesValues may repeat, keys may not
Grows withappend, insertassignment to a new key
A loop hands youthe valuesthe keys
Reach for it whenThe order carries meaning and positions are enoughEach value deserves a name and lookup by name matters
position carries the meaning → list · a name carries the meaning → dictionary
05

Check Yourself

Question
What Does This Print?
The code
stock = {"pen": 4, "book": 2} print(stock.get("cap", 0))
Pick one.
Challenge
Predict Four Outputs

Trace each fragment before pressing Check. The answer button is there for when your trace and the verdict disagree.

type the values separated by spaces, or write None or error when that is what happens
1Counting with a default
counts = {} for ch in "aba": counts[ch] = counts.get(ch, 0) + 1 print(counts["a"])
2Row first, column second
grid = [[1, 2], [3, 4]] print(grid[1][0])
3Rows and columns counted
rows = [[1, 2], [3, 4], [5, 6]] print(len(rows), len(rows[0]))
4A path with three brackets
school = {"ana": {"marks": [7, 9, 5]}} print(school["ana"]["marks"][1])
5A key that was never written
school = {"ana": {"marks": [7, 9, 5]}} print(school["bo"]["marks"][0])
06

Summary

Recap
What You Can Do Now
1
Build a dictionary, read its length, and reach any value by the key that names it.
2
Choose between d[k] and d.get(k), and say what each one does when the key is absent.
3
Add, replace and drop entries with assignment, del, pop and update, and predict the dictionary each one leaves behind.
4
Walk a dictionary with keys, values and items, and count occurrences with a default of zero.
5
Write a matrix as a list of lists, read any cell with grid[row][column], and walk it with nested loops.
6
Read and write a value at the end of a nested path, naming the type each bracket receives along the way.