WasiLearn Code
Python · Data structures

Lists of dictionaries: real data

Every result you get back from a website, an exam system or an exported spreadsheet arrives in the same shape: many things, each described the same way. Thirty students, each with a name and a score. Twelve buses, each with a route and a departure time.

A list holds the many. A dictionary describes each one. Put them together and you have the structure almost all real data uses.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
]
print(students[0])
output
{'name': 'Ayesha', 'score': 88}

Each dictionary in the list is called a record. The list is in order, the keys inside each record are not something you count to — you name them.

Reaching one field

students[0] gave you a whole record. To get one field out of it, add the key in a second set of brackets.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
    {"name": "Chen", "score": 90},
]
print(students[0]["name"])
print(students[1]["score"])
print(len(students))
output
Ayesha
74
3

len(students) counts records, not fields. Three students, however many keys each of them has.

Reading data[0]["name"] without panicking

students[0]["name"] is where beginners stall. Two sets of brackets next to each other look like one strange piece of syntax, so people squint at the whole thing hoping it will resolve, and it never does.

It is not one thing. It is two ordinary steps, left to right, and Python does them in that order. First students[0], which produces a dictionary. Then ["name"] on that dictionary, which produces a string.

Write it out and the mystery disappears:

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
]
first = students[0]
print(first)
name = first["name"]
print(name)
output
{'name': 'Ayesha', 'score': 88}
Ayesha

students[0]["name"] is exactly those two lines with the middle name left out. Whenever a nested expression confuses you, break it apart into named steps and print each one. You can always join it back up afterwards.

The rule to hold on to is that the brackets belong to whatever is immediately to their left, not to the expression as a whole. So the first [0] is a list index, because students is a list. The second ["name"] is a dictionary key, because students[0] is a dictionary.

Get that order wrong and Python says so:

students = [
    {"name": "Ayesha", "score": 88},
]
print(students["name"])

That message — list indices must be integers — is Python telling you that you tried a dictionary move on a list. Read the error as "you are one level out", and count your brackets from the left.

Looping over records

You rarely want record zero. You want all of them. A for loop over a list of dictionaries hands you one dictionary per turn.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
    {"name": "Chen", "score": 90},
]
for student in students:
    print(student["name"], student["score"])
output
Ayesha 88
Bilal 74
Chen 90

Notice there is no [0] anywhere. Inside the loop, student is a record already, so it only needs one set of brackets. Singular name for the loop variable, plural for the list, and you will keep the two levels straight without thinking about it.

An f-string reads better for anything you actually show a person. Mind the quotes: use single quotes for the key when the f-string itself uses double.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
]
for student in students:
    print(f"{student['name']} scored {student['score']}")
output
Ayesha scored 88
Bilal scored 74

Filtering

Filtering means walking every record, testing one field, and collecting the records that pass into a new list.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
    {"name": "Chen", "score": 90},
]
passed = []
for student in students:
    if student["score"] >= 80:
        passed.append(student)
print(passed)
print(len(passed))
output
[{'name': 'Ayesha', 'score': 88}, {'name': 'Chen', 'score': 90}]
2

Three lines of pattern, and you will write it hundreds of times: empty list, loop, if, append. passed is a list of dictionaries too, so everything you have just learned works on it — passed[0]["name"] is Ayesha.

If a key might be missing from some records, student["score"] would crash. student.get("score", 0) gives you a fallback instead, which matters the moment your data comes from somewhere you do not control.

Going deeper than one level

A record's value can itself be a list, or another dictionary. The rule does not change: each set of brackets applies to the thing on its left.

students = [
    {"name": "Fatima", "city": "Lahore", "marks": [72, 90, 65]},
    {"name": "Omar", "city": "Karachi", "marks": [88, 45, 91]},
]
print(students[1]["marks"][0])
print(len(students[0]["marks"]))
output
88
3

Read students[1]["marks"][0] in three beats: record 1, its marks list, the first mark. Say it out loud in that order and the brackets stop looking like a wall.

Counting and adding across records

Totals work the same as any other loop — a running variable outside, and one field pulled from each record inside.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
    {"name": "Chen", "score": 90},
]
total = 0
for student in students:
    total += student["score"]
print(total)
print(total / len(students))
output
252
84.0

The average prints as 84.0 rather than 84 because / in Python always produces a float, even when the division comes out exactly.

Adding a field to every record

Here is the most useful thing in this lesson. Because the loop variable refers to the same dictionary that is sitting in the list — not a copy of it — assigning a new key inside the loop changes the real record.

students = [
    {"name": "Ayesha", "score": 88},
    {"name": "Bilal", "score": 74},
]
for student in students:
    if student["score"] >= 80:
        student["grade"] = "A"
    else:
        student["grade"] = "B"
print(students)
output
[{'name': 'Ayesha', 'score': 88, 'grade': 'A'}, {'name': 'Bilal', 'score': 74, 'grade': 'B'}]

Every record now carries a grade it did not have before, and you never touched students by index. This is the same sharing behaviour that catches people out in the list methods lesson — here it works entirely in your favour.

That single loop is how you enrich real data: read a raw export, walk it once, add the fields you actually need, and hand the list on to whatever draws the report.

Related reading: the dictionaries lesson for keys, .get() and .items(), the list methods lesson for sorting and copying, and the slicing lesson for taking the first few records off a long list.

Prefer learning with a teacher?

WasiLearn Academy runs small-group classes covering this material, online and in Karachi.

See the classes