Question bank — Nested data structures — list of dicts, dict of lists
Before you start, build the picture right here so nothing is deferred. Both structures hold the same grid of cells. The only difference is which axis you slice first:

Look at the figure: on the left, a List of Dicts (LoD) is drawn as a stack of row cards —
each card is one dict {name, age, marks}. On the right, a Dict of Lists (DoL) is drawn as
side-by-side column strips — each strip is one list. Follow the coloured arrows: the single cell
"Ravi's marks = 73" lives at students[1]["marks"] in the LoD and at students["marks"][1] in the
DoL. The two index paths cross over — that crossing is the transpose.
True or false — justify
Every item is a statement. Decide true/false, then give the reason.
LoD and DoL can store exactly the same information.
students[0]["name"] and students["name"][0] always return the same value regardless of structure.
[i][c] for LoD, [c][i] for DoL. The other form raises an error, so they are not interchangeable.In a LoD, the outer container being a list means you must use an integer to index it first.
In a DoL, every column-list must have the same length.
After dol = lod2dol(lod) then back = dol2lod(dol), the check back == lod is True but back is lod is False (all rows sharing keys).
== compares contents (equal cells → equal), so it passes; is compares identity (same object in memory), and back is a freshly built list, so is fails. Round-tripping preserves value, not identity.Round-tripping LoD→DoL→LoD preserves the data even when some rows lack keys.
None), so the rebuilt LoD gains keys the original lacked — == now fails too, not just is.A DoL uses less memory than the equivalent LoD when there are many rows.
You can append a new record to a DoL as easily as to a LoD.
for s in students: iterates rows in a LoD and columns in a DoL.
In modern Python (3.7+) the keys of a DoL come out in a random order when you iterate.
Spot the error
Each line describes code. Say what breaks and why.
students["name"] where students is a list of dicts.
TypeError. You mixed up DoL access on a LoD — use students[0]["name"].students[0] where students is a dict of lists with string keys.
KeyError: 0 — the dict has no key 0; its keys are field names like "name". Use students["name"] first.dol = dict.fromkeys(["name","age"], []) then dol["name"].append("Asha") — trace the object IDs.
id(dol["name"]) == id(dol["age"]) is True: both keys point at the one list built by fromkeys, so after the append both read ["Asha"]. Minimal proof:>>> dol = dict.fromkeys(["name","age"], [])>>> dol["name"].append("Asha")>>> dol → {'name': ['Asha'], 'age': ['Asha']}. Fix with {c: [] for c in cols}, where each [] is a fresh object with its own id — see Mutable vs Immutable.{k: [row[k] for row in lod] for k in lod[0]} when one row is {"name":"Ravi"} (no "marks").
KeyError: "marks" on that row. The comprehension trusts every row to share lod[0]'s keys. Use row.get(k) for a None default.for s in students: students.remove(s) to clear a LoD.
students = [s for s in students if s["marks"] >= 40], keeping only rows whose condition holds (here, pass marks) rather than deleting in place.avg = sum(students["marks"]) on a list of dicts.
students["marks"] already fails with TypeError (list indexed by string). Column math only works after building the column, or in DoL directly.n = len(students) used as the row count of a dict of lists.
len(students) on a DoL counts columns (number of keys), not rows. Row count is len(students[any_key]) — the length of one column.row["age"] += 5 inside for row in students where students is a DoL.
row is a string key like "age", not a dict, so row["age"] raises TypeError. Iterating a dict gives keys — to touch a cell use students["age"][i].Why questions
Answer the reasoning, not just a fact.
Why does DoL win for computing a column average?
sum(col)/len(col) runs directly — no gathering across records with a comprehension first.Why does LoD win for adding or deleting a whole record?
append (add) or list-filtering (remove) touches exactly one object, not every column.Why do the indices swap order between the two forms ([i][c] vs [c][i])?
Why is LoD the natural match for JSON from an API?
Why can iterating a DoL with for x in students bite beginners?
Why does the shared-list bug only appear with dict.fromkeys(cols, []) and not with a dict comprehension?
fromkeys evaluates the [] once and points every key at that one object; the comprehension {c: [] for c in cols} runs [] fresh on each iteration, giving independent lists.Why must LoD→DoL conversion collect keys carefully when rows may differ?
lod[0]'s keys you miss fields other rows have; the safe key set is {k for row in lod for k in row} over all rows.Why is a Pandas DataFrame often described as a "turbocharged DoL"?
Edge cases
Boundary and degenerate inputs — the scenarios people forget.
An empty LoD []: what does lod[0] do, and how do you get its keys?
lod[0] raises IndexError (no row 0) and there are no keys to read — an empty LoD has no schema. Guard with if lod: before touching lod[0].An empty DoL {}: how many rows does it have?
len(dol) is 0 (zero columns), and row count is unknowable until at least one key exists.students[-1] on a LoD vs students[-1] on a DoL — what does each mean?
-1 is the last row (valid, students[len-1]). On a DoL the outer container is a dict, so -1 is treated as a key; unless a key literally -1 exists it raises KeyError. Negative indexing only works on the list layer.students[1:3] (a slice) on a LoD vs on a DoL — which works?
TypeError — dicts are not sliceable by start:stop. To slice rows in a DoL you must slice each column: {c: students[c][1:3] for c in students}.A DoL where columns have different lengths ("name" len 3, "age" len 2): is row 2 valid?
"name"; asking dol["age"][2] raises IndexError. The structure is malformed — every column must share the row count.A LoD where row 0 has extra keys the others lack: does {k: [row[k] ...] for k in lod[0]} work?
KeyError. Use .get(k) or a full key set.A LoD with a single record [{"a":1}]: is it still a valid LoD, and its DoL?
{"a":[1]} — every column becomes a length-1 list. The transpose relationship still holds.A DoL with a single column {"a":[1,2,3]}: what is its LoD form?
[{"a":1},{"a":2},{"a":3}] — three rows, each a one-field dict. Row count came from the single column's length.Duplicate field values across rows (two students named "Asha") — does either structure break?
A field value that is itself a list (e.g. "marks":[88,73] per student in a LoD): does access change?
students[0]["marks"] still returns that inner list; you just index one level deeper (students[0]["marks"][1]) to reach an element. Nesting composes cleanly.Connections
- Lists — the integer-indexed container behind LoD's outer and DoL's inner
- Dictionaries — key→value lookup powering both
- List Comprehensions — the transpose engine and the ragged-key traps
- JSON and APIs — why list-of-dicts is the common wire format
- Pandas DataFrame — column-first storage, the DoL taken to production
- Loops and Iteration — why iterating a dict vs a list surprises you
- Mutable vs Immutable — the root of the shared-inner-list bug