runtime
81 / 250
lesson 81 Nested Data Structures · intermediate

Read a value deep inside a response

An API response is a dictionary holding lists holding more dictionaries. A chain of keys and indexes, read left to right, reaches any value inside it.

You'll be able to
  • Read a chain like page["features"][0]["properties"]["mag"] one step at a time
  • Use a key on a dictionary and a position on a list, and recognise the error when you mix them up
  • Write a function that pulls several values out of one nested record
The idea

The US Geological Survey publishes every earthquake as a GeoJSON record. One record is a dictionary; its properties key holds another dictionary (magnitude, place), and its geometry key holds a dictionary whose coordinates is a list: longitude, latitude, depth in km.

Nothing new is needed to read inside it. quake["properties"] gives back a dictionary, and you can put another pair of brackets straight after it: quake["properties"]["place"]. Each pair of brackets works on the value the previous pair produced, left to right.

Run each example to see what it prints. Running examples doesn't count toward progress.

example-1.py · One earthquake from the USGS feed
quake = {
    "id": "us6000pw77",
    "properties": {"mag": 4.7, "place": "Arctic Ocean"},
    "geometry": {"type": "Point", "coordinates": [-107.5679, 80.7069, 10]},
}
print(quake["properties"]["place"])
print(quake["properties"]["mag"])
coordinates = quake["geometry"]["coordinates"]
print(coordinates)
print("depth:", quake["geometry"]["coordinates"][2], "km")

Each step must match the kind of value it lands on: a key for a dictionary, a position for a list. A page of results keeps its events in a list under features, so you need a position before you can ask for properties.

example-2.py · A key where a position belongs
page = {"metadata": {"count": 1}, "features": [{"id": "us6000pw77", "properties": {"mag": 4.7}}]}
print(page["metadata"]["count"])
print(page["features"]["properties"]["mag"])

In a program, the chain lives in a function that takes the whole response and returns just what the caller needs. Check for an empty list before you reach for position 0, because an API can return a page with no events.

example-3.py · Summarise a page of results
def first_event(page):
    if len(page["features"]) == 0:
        return "no events"
    first = page["features"][0]
    place = first["properties"]["place"]
    depth = first["geometry"]["coordinates"][2]
    return f"{page['metadata']['count']} events; first: {place}, {depth} km deep"

page = {
    "metadata": {"count": 2},
    "features": [
        {"properties": {"place": "233 km E of Levuka, Fiji"}, "geometry": {"coordinates": [-178.4779, -18.0069, 585.411]}},
        {"properties": {"place": "Southern Alaska"}, "geometry": {"coordinates": [-147.2371, 61.4029, 9.6]}},
    ],
}
print(first_event(page))
print(first_event({"metadata": {"count": 0}, "features": []}))

When to use it: any time data comes as JSON from an API or a file: orders with their lines, a user with their address, a page of search results. Mistakes to avoid: a key where a list needs a position (the TypeError above), position 0 of an empty list (IndexError), and, with GeoJSON, reading position 0 as the latitude: the format stores longitude first.

Review
  • Each pair of brackets works on the value the previous one produced: keys for dictionaries, positions for lists.
  • A key on a list gives TypeError: list indices must be integers…; position 0 of an empty list gives IndexError.
  • GeoJSON coordinates are longitude, latitude, depth.
  • Next: real records often leave keys out. get reads a key that might be missing.

Loading your progress…

To master this lesson, solve both challenges without hints or the answer, and pass the concept check.

Read a value deep inside a response — Nested Data Structures · Runtime