1.2.26 · D3 · HinglishIntroduction to Programming (Python)

Worked examplesNested data structures — list of dicts, dict of lists

3,324 words15 min read↑ Read in English

1.2.26 · D3 · Coding › Introduction to Programming (Python) › Nested data structures — list of dicts, dict of lists

Kisi bhi example se pehle hum apna running data agree kar lete hain taaki har symbol earned ho:

students_lod = [
    {"name": "Asha",  "age": 20, "marks": 88},
    {"name": "Ravi",  "age": 22, "marks": 73},
    {"name": "Meera", "age": 21, "marks": 95},
]
students_dol = {
    "name":  ["Asha", "Ravi", "Meera"],
    "age":   [20, 22, 21],
    "marks": [88, 73, 95],
}

Dono mein same teen students hain. students_lod bahar se list hai; students_dol bahar se dict hai. Yeh ek akela difference — ki outer container kya hai — neeche ke har access rule ko decide karta hai.


The scenario matrix

Neeche ki table ek checklist hai jise tum tick karte ho, sirf summary nahi. Isse left se right padho, ek row ek baar:

  • # column ek label hai (A–J) — ek tarah ki problem ka short naam, taaki hum baad mein iske taraf point kar sakein ("yeh Case F hai").
  • Case class plain words mein batata hai ki us row mein kaunsi situation describe ho rahi hai.
  • What makes it tricky woh trap hai jo us situation mein chhupa hai — woh reason jiske liye iska apna example deserve karta hai.
  • Covered by exactly batata hai ki neeche kaunsa worked example us trap ko ground mein drive karta hai.

Kaise use karein: koi bhi row uthao, apne haath se last column dhako, aur predict karne ki koshish karo ki trap kya hai aur tum use kaise handle karoge. Phir matching example padho aur apne aap ko check karo. Jab har row obvious lagne lage, tum iss topic ke har scenario ko cover kar chuke hoge.

# Case class What makes it tricky Covered by
A Clean LoD read (row → field) integer pehle, key baad mein index karna zaroori Example 1
B Clean DoL read (column → row) key pehle, integer baad mein index karna zaroori Example 1
C Column math (average / max) DoL shines karta hai, LoD ko gather karna padta hai Example 2
D Structure conversion LoD ↔ DoL "transpose" — indices cross over ho jaate hain Example 3
E Missing key (ragged records) KeyError ka risk — default chahiye Example 4
F Empty container (zero rows) division by zero / IndexError edge Example 5
G Wrong index order (classic bug) TypeError vs KeyErrorkaunsa aur kyun Example 6
H Shared-inner-list trap DoL banate waqt ek list object, saare keys milke mutate ho jaate hain Example 7
I Real-world word problem (filter + aggregate) JSON-from-API style, sahi shape chuno Example 8
J Exam-style twist (mutate while iterating) undefined behaviour, safe rebuild Example 9

Neeche produce hue har numeric answer ko verify block mein machine-check kiya gaya hai.


Example 1 — Cases A & B: do coordinate systems

Steps.

  1. students_lod[1] → dict {"name": "Ravi", "age": 22, "marks": 73}. Yeh step kyun? Outer container ek list hai, isliye woh sirf integer position samajhta hai. Index 1 ka matlab hai "doosri row" (0 se ginte hue).
  2. ... ["age"]22. Yeh step kyun? Step 1 se mili value ek dict hai, isliye agla bracket ek key lookup hai, position nahi.
  3. Ab DoL: students_dol["age"][20, 22, 21]. Yeh step kyun? Outer container ek dict hai, isliye pehla bracket ek key hona chahiye — yeh poora age column wapas deta hai.
  4. ... [1]22. Yeh step kyun? Woh column ek list hai, isliye hum ek integer position se finish karte hain.

Neeche ka figure dekho: same yellow cell (row 1, age = 22) tak do alag arrows pahunchte hain — aur do index tokens opposite order mein hain. Woh mirror hi poora idea hai.

Figure — Nested data structures — list of dicts, dict of lists

Figure 1 — 3×3 student table. Red arrow (LoD) upar se enter karta hai: pehle integer [1] row 1 pick karta hai, phir ["age"] column pick karta hai. Green arrow (DoL) neeche se enter karta hai: pehle ["age"] column pick karta hai, phir [1] row pick karta hai. Dono arrows same highlighted cell 22 par land karte hain, aur neeche yellow caption yaad dilata hai ki do index tokens order swap karte hain.

Recall

students_lod[1]["age"] return karta hai ::: 22 (row 1 ka dict, phir uska "age" field) students_dol["age"][1] return karta hai ::: 22 ("age" column, phir position 1) — same cell, indices swapped


Example 2 — Case C: column math (average marks)

Steps.

  1. DoL tarika: sum(students_dol["marks"])88 + 73 + 95 = 256. Yeh step kyun? Column already ek flat list hai, isliye sum seedha iske upar run karta hai — koi digging nahi.
  2. Divide karo: 256 / 3 = 85.333.... Yeh step kyun? Teen students, isliye len(students_dol["marks"]) == 3.
  3. LoD tarika: pehle gather karna padega — [s["marks"] for s in students_lod][88, 73, 95]. Yeh step kyun? LoD mein marks alag-alag row dicts mein bikhra hua hai. Comprehension har row visit karta hai aur uska "marks" ek list mein pull karta hai. Tabhi hum sum kar sakte hain.
  4. sum([88, 73, 95]) / 3 → same 85.333....

Verify: . DoL path ne kam kaam kiya (koi gathering step nahi) — exactly isliye column aggregates DoL prefer karte hain.


Example 3 — Case D: the transpose (LoD → DoL → LoD)

Steps.

  1. LoD → DoL:
    dol = {k: [row[k] for row in students_lod] for k in students_lod[0]}
    Yeh step kyun? Outer comprehension keys (name, age, marks) par walk karta hai, jo pehli row se lete hain kyunki saari rows keys share karti hain. Inner comprehension har row par walk karta hai aur us key ki value row order mein collect karta hai. Result: {"name": [...], "age": [...], "marks": [...]}.
  2. Isse dol["age"] == [20, 22, 21] milta hai — age column, order mein. Yeh step kyun? Sanity check: column ko original ages ko top-to-bottom match karna chahiye.
  3. DoL → LoD (rotate back):
    keys = list(dol)                 # ["name","age","marks"]
    n = len(dol["name"])             # 3 rows
    lod2 = [{k: dol[k][i] for k in keys} for i in range(n)]
    Yeh step kyun? Hum row index i ko 0..n-1 tak loop karte hain; har i ke liye hum ek dict banate hain har column ka i-th element read karke. Yeh data[c][i]data[i][c] hai — swap reverse mein.
  4. Compare karo: lod2 == students_lod?

Verify: round trip original list of dicts exactly return karta hai — lod2 == students_lod True hai. Do baar rotate karne par ghar wapas aate hain, confirm karta hai ki LoD aur DoL identical information carry karte hain, bas 90° rotate — parent note mein introduce kiya gaya "transpose" idea.


Example 4 — Case E: ek missing key (ragged records)

Steps.

  1. Outer loop ragged[0] se keys leta hai → ["name", "age"]. Yeh step kyun? Yeh sirf pehli row ki keys dekhta hai; assume karta hai ki saari rows match karti hain.
  2. "age" column banate waqt Meera ka dict visit hota hai aur row["age"] evaluate hota hai. Yeh step kyun? Yahaan assumption toot jaata hai — Meera ke paas "age" nahi hai.
  3. Result: KeyError: 'age'. Poora conversion mar jaata hai. Yeh step kyun? [...] se ek missing dict key hard error hai, silent None nahi.
  4. Fix .get se:
    dol = {k: [row.get(k) for row in ragged] for k in ragged[0]}
    Ab row.get("age") Meera ke liye explode karne ki jagah None return karta hai.

Verify: fixed version dol["age"] == [20, 22, None] deta hai. None ek visible flag hai jo kehta hai "yeh cell absent tha" — crash se bahut zyada safe.


Example 5 — Case F: empty container (zero rows)

Steps.

  1. DoL: sum(empty_dol["marks"])0 (kisi cheez ka sum 0 hai, by definition). Yeh step kyun? Empty list par sum ek defined, safe 0 hai.
  2. len(empty_dol["marks"])0. Yeh step kyun? Zero rows ka matlab zero-length columns.
  3. 0 / 0ZeroDivisionError. Yeh step kyun? Zero items ka average undefined hai — language guess karne se mana karti hai.
  4. Guard karo:
    n = len(empty_dol["marks"])
    avg = sum(empty_dol["marks"]) / n if n else 0.0
    Yeh step kyun? Hum divide karne se pehle if n (nonzero) test karte hain aur ek sensible default return karte hain.
  5. LoD trap variant: empty_lod[0]IndexError. Yeh step kyun? Koi row 0 nahi hai padne ke liye; pehli row index karne se pehle hamesha if empty_lod: check karo.

Verify: guard ke saath, empty class ke liye avg == 0.0 — koi crash nahi. Empty-input handling data code mein sabse zyada bhula diya jaane wala case hai; hamesha pucho "agar zero rows hon toh?"


Example 6 — Case G: wrong index order (TypeError vs KeyError)

Steps.

  1. students_lod["name"] — outer container ek list hai. Yeh step kyun? Ek list sirf integer position se index hoti hai. Use string "name" ka position ke roop mein koi andaaza nahi.
  2. Result: TypeError: list indices must be integers or slices, not str. Yeh step kyun? Galat type ka index → TypeError (KeyError nahi). Yeh ek precise tell hai: yahaan TypeError dekhna matlab "tumne ek list ko string diya."
  3. Ab mirror mistake: dict par students_dol[0]. Yeh step kyun? Ek dict key se index hoti hai. 0 students_dol mein ek key nahi hai.
  4. Result: KeyError: 0. Yeh step kyun? Sahi mechanism (key lookup), missing key → KeyError.
  5. Fix: outer container ki maano. LoD → integer pehle (students_lod[0]["name"]). DoL → key pehle (students_dol["name"][0]).

Verify: dono fixed forms "Asha" return karte hain. Exception ka type tumhara diagnostic hai: TypeError = string ek list mein; KeyError = dict mein galat key.


Example 7 — Case H: shared-inner-list trap

Steps.

  1. dict.fromkeys(cols, []) default value list ko ek baar create karta hai aur har key ko us same object ki taraf point karta hai. Yeh step kyun? fromkeys default copy nahi karta — saari keys ek list ko alias karti hain.
  2. bad["name"].append("Asha") us shared list ko mutate karta hai. Yeh step kyun? Kyunki bad["name"] aur bad["marks"] same list hain, append dono keys ke neeche dikhta hai.
  3. Result: bad["marks"] == ["Asha"] bhi — ek phantom value us column mein appear hui jise tumne kabhi touch nahi kiya. Yeh step kyun? Aliasing (dekho Mutable vs Immutable): ek object, do naam.
  4. Fix:
    good = {c: [] for c in cols}   # har key ke liye FRESH list
    good["name"].append("Asha")
    Yeh step kyun? Dict comprehension [] ko har key ke liye ek baar run karta hai, independent lists create karta hai.

Verify: good ke saath, "name" mein append karne se good["marks"] == [] rehta hai; bad ke saath woh galat taur par ["Asha"] ban jaata hai. Fresh-per-key hi DoL scratch se banane ka akela safe tarika hai.


Example 8 — Case I: real-world word problem

Steps.

  1. Shape chuno: data list of dicts ke roop mein aaya (typical JSON array of objects). Hum rows filter kar rahe hain, isliye LoD natural fit hai — har row ek self-contained record hai. Yeh step kyun? "Kaunse records qualify karte hain" filter karna ek row operation hai; LoD har row ko puri rakhta hai.
  2. Filter: passed = [s for s in students_lod if s["marks"] >= 80]. Yeh step kyun? Comprehension sirf woh rows rakhta hai jo condition meet karte hain → Asha aur Meera.
  3. Ages gather karo: ages = [s["age"] for s in passed][20, 21]. Yeh step kyun? Ab rows choose ho gayi hain, sirf woh field pull karo jo hume aggregate karna hai.
  4. Average: sum(ages) / len(ages)41 / 2 = 20.5. Yeh step kyun? Do passing students; len(ages) guard karo agar filter empty ho sakta ho (Case F!).

Verify: average passing age years (units: years, kyunki humne ages average kiye). LoD par filter-then-aggregate everyday API workflow hai — aur ek natural feeder Pandas DataFrame mein agar data grow kare.


Example 9 — Case J: exam twist (mutate while iterating)

Steps.

  1. Naive loop trace karo. Yeh ek internal position counter se chalata hai jo 0 se start hota hai. Counter 0 par Asha (20 < 21) dikhti hai aur use remove karta hai; ab baad ke har element ek left shift ho jaata hai, isliye Ravi position 0 par hai, Meera 1 par, Kiran 2 par. Yeh step kyun? Iteration ke dauran remove karna loop ke counter ko (ab chhoti) list se desynchronise kar deta hai.
  2. Loop counter ko 1 tak advance karta hai — lekin shift ke baad, position 1 par Meera (21) hai, Kiran nahi. Ravi position 0 par tha — use completely jump over kar diya gaya. Yeh step kyun? Kyunki indices loop ke paon ke neeche move ho gayi, woh element jo newly-vacated slot mein slide hua woh kabhi examine nahi hota.
  3. Loop end tak pahunchta hai bina Kiran (19) ko kabhi test kiye, isliye Kiran bach jaata hai bhale hi use delete hona chahiye tha — yeh ek silent wrong answer hai, crash nahi. Yeh step kyun? Yeh classic "mutate-while-iterating" bug hai Loops and Iteration ka; traversal aur mutation ek doosre se ladte hain, effectively undefined results dete hain.
  4. Fix — rebuild karo, mutate mat karo:
    people = [p for p in people if p["age"] >= 21]
    Yeh step kyun? Hum ek nayi list banate hain ek fresh, complete pass se, isliye original ko mid-iteration disturb nahi kiya jaata. Har element exactly ek baar test hota hai.

Verify: comprehension exactly ["Ravi", "Meera"] deta hai (dono age >= 21), yani 2 survivors. Rebuild sahi hai jahaan in-place removal silently Kiran (19) ko rakh leta — yahi Case J ka poora point hai.


Connections

  • Parent topic
  • Lists — LoD ka integer-indexed outer container
  • Dictionaries — key-indexed container; .get Example 4 bachata hai
  • List Comprehensions — gather / filter / transpose engine
  • JSON and APIs — Example 8 ke list-of-dicts ka source
  • Pandas DataFrame — jahaan column math (Example 2) scale up hota hai
  • Loops and Iteration — mutate-while-iterating trap (Example 9)
  • Mutable vs Immutable — shared-list aliasing bug (Example 7)

Concept Map

reads

aggregates

converts

edge inputs

mutation traps

Scenario matrix A to J

Cases A B G index order

Cases C I column math

Case D transpose

Cases E F ragged and empty

Cases H J aliasing and iterate