Twenty lessons of primitives. Today: compose six of them on a small generic input — no new concepts, just careful arrangement.
Given a CSV file with columns id,date,value:
id,date,value
a1,2024-01-15,10
b2,2024-01-22,15
c3,2025-06-01,20
d4,2024-12-31,5
Produce a JSON file mapping "YYYY-MM" → list of values, in insertion order:
{
"2024-01": [10, 15],
"2025-06": [20],
"2024-12": [5]
}Read CSV, parse the date, format as YYYY-MM, accumulate values into a dict keyed by that month-string, write JSON.
Right. Six steps, six primitives:
| Step | Primitive | Lesson |
|---|---|---|
| 1. Write the CSV (so we have a fixture) | file write + "w" mode | L2, L3 |
| 2. Read CSV rows as dicts | csv.DictReader | L15 |
| 3. Parse each date | datetime.fromisoformat | L19 |
4. Format as YYYY-MM | .strftime("%Y-%m") | L19 |
| 5. Accumulate per-key values | dict + setdefault (or if key in result) | L11 |
| 6. Write JSON | json.dump | L16 |
And the value-list per month — it's a list, so I need to append not overwrite.
Right. The shape result.setdefault(key, []).append(value) does both things at once: ensures the key has a list (creating an empty one if missing), then appends to it. We didn't introduce setdefault directly — it's the dict equivalent of "if key in d: ... else: d[key] = []; d[key].append(...)".
And the value int(row["value"]) — because csv returns strings.
That's the only conversion. After the loop, json.dump(result, f) writes the final dict.
| Primitive | From | Used for |
|---|---|---|
open(..., "w") and f.write(...) | L2, L3 | creating the fixture CSV file |
with open(...) as f: | L4 | guaranteed close |
csv.DictReader(f) | L15 | iterating CSV rows as dicts |
datetime.fromisoformat(s) | L19 | turning the date string into a datetime |
d.strftime("%Y-%m") | L19 | formatting back to a YYYY-MM group key |
result.setdefault(key, []) | new — see below | ensure key has a list before appending |
int(...) | foundations | str → int for the numeric value |
json.dump(result, f) | L16 | writing the result file |
dict.setdefault(key, default) — get or createA shorthand for the "if key in d, use d[key], else create it" pattern:
result = {}
result.setdefault("a", []) # 'a' wasn't there → set d['a'] = [] and return it
result.setdefault("a", []) # 'a' is there → return d['a'] (the existing list)Returns the value at the key (existing or freshly created). The classic pattern for grouping:
result = {}
for key, value in pairs:
result.setdefault(key, []).append(value)Reads: "Make sure result[key] is a list, then append value to it."
(You'll see an even cleaner form in week 4 with defaultdict. Today: explicit setdefault.)
import csv, json
from datetime import datetime
# Step 1: write fixture
with open("events.csv", "w") as f:
f.write("id,date,value\n")
f.write("a1,2024-01-15,10\n")
f.write("b2,2024-01-22,15\n")
f.write("c3,2025-06-01,20\n")
f.write("d4,2024-12-31,5\n")
# Step 2-5: read, parse, group
result = {}
with open("events.csv") as f:
for row in csv.DictReader(f):
d = datetime.fromisoformat(row["date"])
key = d.strftime("%Y-%m")
result.setdefault(key, []).append(int(row["value"]))
# Step 6: write JSON
with open("out.json", "w") as f:
json.dump(result, f)This shape — read rows, derive a key per row, accumulate values into a per-key list — comes up constantly in real Python. CSV → grouped JSON, JSON logs → daily summaries, lines of input → bucket-by-something. Today's lesson is one variant; you'll write the same shape with different keys and accumulators a hundred times.
Twenty lessons of primitives. Today: compose six of them on a small generic input — no new concepts, just careful arrangement.
Given a CSV file with columns id,date,value:
id,date,value
a1,2024-01-15,10
b2,2024-01-22,15
c3,2025-06-01,20
d4,2024-12-31,5
Produce a JSON file mapping "YYYY-MM" → list of values, in insertion order:
{
"2024-01": [10, 15],
"2025-06": [20],
"2024-12": [5]
}Read CSV, parse the date, format as YYYY-MM, accumulate values into a dict keyed by that month-string, write JSON.
Right. Six steps, six primitives:
| Step | Primitive | Lesson |
|---|---|---|
| 1. Write the CSV (so we have a fixture) | file write + "w" mode | L2, L3 |
| 2. Read CSV rows as dicts | csv.DictReader | L15 |
| 3. Parse each date | datetime.fromisoformat | L19 |
4. Format as YYYY-MM | .strftime("%Y-%m") | L19 |
| 5. Accumulate per-key values | dict + setdefault (or if key in result) | L11 |
| 6. Write JSON | json.dump | L16 |
And the value-list per month — it's a list, so I need to append not overwrite.
Right. The shape result.setdefault(key, []).append(value) does both things at once: ensures the key has a list (creating an empty one if missing), then appends to it. We didn't introduce setdefault directly — it's the dict equivalent of "if key in d: ... else: d[key] = []; d[key].append(...)".
And the value int(row["value"]) — because csv returns strings.
That's the only conversion. After the loop, json.dump(result, f) writes the final dict.
| Primitive | From | Used for |
|---|---|---|
open(..., "w") and f.write(...) | L2, L3 | creating the fixture CSV file |
with open(...) as f: | L4 | guaranteed close |
csv.DictReader(f) | L15 | iterating CSV rows as dicts |
datetime.fromisoformat(s) | L19 | turning the date string into a datetime |
d.strftime("%Y-%m") | L19 | formatting back to a YYYY-MM group key |
result.setdefault(key, []) | new — see below | ensure key has a list before appending |
int(...) | foundations | str → int for the numeric value |
json.dump(result, f) | L16 | writing the result file |
dict.setdefault(key, default) — get or createA shorthand for the "if key in d, use d[key], else create it" pattern:
result = {}
result.setdefault("a", []) # 'a' wasn't there → set d['a'] = [] and return it
result.setdefault("a", []) # 'a' is there → return d['a'] (the existing list)Returns the value at the key (existing or freshly created). The classic pattern for grouping:
result = {}
for key, value in pairs:
result.setdefault(key, []).append(value)Reads: "Make sure result[key] is a list, then append value to it."
(You'll see an even cleaner form in week 4 with defaultdict. Today: explicit setdefault.)
import csv, json
from datetime import datetime
# Step 1: write fixture
with open("events.csv", "w") as f:
f.write("id,date,value\n")
f.write("a1,2024-01-15,10\n")
f.write("b2,2024-01-22,15\n")
f.write("c3,2025-06-01,20\n")
f.write("d4,2024-12-31,5\n")
# Step 2-5: read, parse, group
result = {}
with open("events.csv") as f:
for row in csv.DictReader(f):
d = datetime.fromisoformat(row["date"])
key = d.strftime("%Y-%m")
result.setdefault(key, []).append(int(row["value"]))
# Step 6: write JSON
with open("out.json", "w") as f:
json.dump(result, f)This shape — read rows, derive a key per row, accumulate values into a per-key list — comes up constantly in real Python. CSV → grouped JSON, JSON logs → daily summaries, lines of input → bucket-by-something. Today's lesson is one variant; you'll write the same shape with different keys and accumulators a hundred times.
Create a free account to get started. Paid plans unlock all tracks.