Records: lists of dicts and dicts of lists
Part of the free LogicWiz Python course, Module 6: Dictionaries and sets. The examples and practice run in the app.
The board practical says: "Store the names and marks of n students and display the names of students who scored above 75." With one dict per student, that is a three-line loop.
You'll learn to
- Store each record as a dict, and a table as a list of dicts
- Use a dict of lists when you think in columns
- Group with `setdefault`, start counters with `fromkeys`, and sort records by a field
Learn
One dict per record
A record is everything about one thing: one student, one order. A dict fits it well, because each field gets a name:
{"name": "Aarav", "section": "A", "marks": 82}
A whole class is then a list of dicts, one dict per student. Reading s["marks"] is much clearer than remembering that marks were at index 2 of a list.
Students with marks above 75
students = [
{"name": "Aarav", "marks": 82},
{"name": "Meher", "marks": 71},
{"name": "Riya", "marks": 94},
]
for s in students:
if s["marks"] > 75:
print(s["name"], s["marks"])
Dict of lists: one list per column
marks = {"Maths": [78, 66, 88], "CS": [90, 58, 95]}
for subject, scores in marks.items():
print(subject, max(scores), round(sum(scores) / len(scores), 1))
marks["CS"].append(70)
print(marks["CS"])
Here the key is a subject and the value is a whole list of scores. Good when you work column by column (this is also how a pandas table is built, in Module 10).
Which shape?
| List of dicts | Dict of lists |
|---|---|
| [{"name": "Aarav", "marks": 82}, …] | {"name": ["Aarav", …], "marks": [82, …]} |
| one item per row (record) | one item per column (field) |
| easy to add, filter or sort students | easy to total or average a whole column |
| students[0]["marks"] | table["marks"][0] |
Records by roll number
db = {101: {"name": "Aarav", "marks": 82},
102: {"name": "Meher", "marks": 71}}
print(db[102]["name"])
db[101]["marks"] += 5
print(db[101])
print(103 in db)
A dict of dicts gives you instant lookup by roll number, then a field by name: db[roll][field].
setdefault and fromkeys
pairs = [("Aarav", "A"), ("Meher", "B"), ("Riya", "A")]
sections = {}
for name, sec in pairs:
sections.setdefault(sec, []).append(name)
print(sections)
att = dict.fromkeys(["Aarav", "Meher", "Riya"], 0)
att["Meher"] += 1
print(att)
setdefault(key, default) returns the value for key, first storing default if the key is missing. Perfect for grouping. dict.fromkeys(keys, value) makes a dict with every key set to the same starting value.
Sorting records by a field
students = [{"name": "Aarav", "marks": 82},
{"name": "Meher", "marks": 71},
{"name": "Riya", "marks": 94}]
def by_marks(s):
return s["marks"]
for s in sorted(students, key=by_marks, reverse=True):
print(s["name"], s["marks"])
key= takes a function that turns each record into the value to sort by. Python calls by_marks on each dict and sorts by what it returns.
dict.fromkeys(["x", "y"], []) puts the same empty list under every key (the aliasing trap again), so appending to one appends to all. Use fromkeys only with values you won't change in place, like 0 or "", and use setdefault(k, []) for lists.
CBSE Class 11/12 practical: "Input the names and marks of n students into a dictionary and display the names of students with marks above 75." Read n, loop n times with input(), store record[name] = marks, then loop over the dict and print the ones that pass the test.
In one line
A record is a dict, a table is a list of dicts; group with d.setdefault(k, []).append(x); use fromkeys only with immutable starting values.