← Home

063. Nested Data Structures

Compose data structures for complex models

063. Nested Data Structures

Each process in Aryan’s monitor is a dict with nested data:

proc = {
    "name":    "Chrome",
    "pid":     4521,
    "memory":  {"rss_mb": 512.3, "vms_mb": 1024.0},
    "threads": [
        {"id": 101, "cpu_pct": 4.2},
        {"id": 102, "cpu_pct": 2.1},
    ],
    "tags":    {"browser", "high_priority"},
}

Accessing nested data requires chained indexing:

rss        = proc["memory"]["rss_mb"]         # dict → dict
first_tid  = proc["threads"][0]["id"]          # dict → list → dict
all_cpus   = [t["cpu_pct"] for t in proc["threads"]]
total_cpu  = sum(all_cpus)

A whole snapshot is a list of these dicts, matching the shape of a real API response (e.g., psutil.process_iter() results serialized as JSON).

The bug that always trips beginners: sum(proc) on a dict iterates the string keys — TypeError. You need sum(proc["memory"].values()) or access the specific nested field.


💡 Fun fact: The “list of dicts” structure Aryan is using is identical to JSON (JavaScript Object Notation), the web’s universal data exchange format. When you call json.loads() on an API response, Python converts JSON objects to dicts and JSON arrays to lists — producing exactly this nested structure. This is why Python is so dominant for REST API clients and web scraping: the data arrives in JSON and is immediately usable as native Python dicts and lists.

⚠️ Watch out: Nested dicts and lists are passed by reference — modifying a nested object inside a function mutates the original. student["grades"].append(99) inside a helper function permanently changes the caller’s data. Use copy.deepcopy(student) to get a fully independent copy when you need to modify nested structures without affecting the original.

🤔 Think about it: average_grade(student) accesses student["grades"] assuming that key always exists. What happens if some students have a "grades" key and others don’t — perhaps new students with no grades yet? How would you make average_grade robust against missing or empty grades without crashing?

Learning objectives

  • Access data in nested dicts and lists
  • Build and process list-of-dicts records
  • Navigate nested structures with multiple indexing levels

Key concepts

  • nested data structures
  • list of dicts
  • nested access

Try it

Concept detail

Nested data structures combine lists and dicts for real-world models.

Common patterns: list of dicts — [{…}, {…}] — database rows, API responses, process snapshots dict of lists — {“alice”: [90,85], “bob”: [70,75]} — grouped data dict of dicts — {“config”: {“host”: “…”, “port”: 5432}} — nested config list of lists — [[1,2],[3,4]] — matrix / grid data

Accessing nested data: data[0][“name”] # first record’s name data[0][“grades”][2] # first record’s third grade config[“db”][“host”] # two levels of dict

Building nested structures: [{“x”: i, “y”: i*2} for i in range(5)]

Mutation warning: changing a nested dict mutates the original because dicts are passed by reference. Use copy.deepcopy() for true independence.

This shape (list of dicts) is identical to JSON — json.loads() and json.dumps() convert between Python nested structures and JSON strings directly.

Solution

def average_grade(student):
    grades = student["grades"]
    return sum(grades) / len(grades)

def top_student(students):
    best = None
    best_avg = -1
    for student in students:
        avg = average_grade(student)
        if avg > best_avg:
            best_avg = avg
            best = student["name"]
    return best

def students_above(students, threshold):
    result = []
    for student in students:
        if average_grade(student) >= threshold:
            result.append(student["name"])
    return result

Tests

STUDENTS = [
    {"name": "Alice",   "grades": [90, 85, 92]},
    {"name": "Bob",     "grades": [70, 75, 68]},
    {"name": "Charlie", "grades": [95, 98, 100]},
]

def test_average_alice():
    assert abs(average_grade(STUDENTS[0]) - 89.0) < 0.01

def test_average_bob():
    assert abs(average_grade(STUDENTS[1]) - 71.0) < 0.01

def test_top_student():
    assert top_student(STUDENTS) == "Charlie"

def test_top_student_returns_string():
    result = top_student(STUDENTS)
    assert type(result) == str  # must be the name, not the whole dict

def test_students_above():
    result = students_above(STUDENTS, 80)
    assert "Alice" in result
    assert "Charlie" in result
    assert "Bob" not in result

def test_students_above_none():
    assert students_above(STUDENTS, 99) == []

Resources