Capstone: Expense Tracker (data layer)
For the next two lessons you build a real thing: an Expense Tracker, the engine behind a tiny command-line money app. No fill-in-the-blank. You get a plain-English spec and a near-blank file, and you architect the rest. Part 1 is the data layer -> four pure functions over a ledger. Part 2 wraps them in a runnable app you can drive with commands.
A ledger is just a list of dicts. One expense looks like this:
{"description": "Coffee", "amount": 3.5, "category": "food"}Pure functions take data in and hand data back. No printing, no globals, no surprises -> which is exactly why they are a joy to test. Build these four:
add_expense(ledger, desc, amount, category)
Returns a new ledger -> the old one plus one fresh expense dict (keys "description", "amount", "category"). Do not mutate the list you were handed; return a new one with ledger + [new_dict]. That immutability keeps callers safe.
ledger = []
ledger = add_expense(ledger, "Coffee", 3.5, "food")
ledger = add_expense(ledger, "Bus", 2.0, "transport")total(ledger)
The grand total of every amount. An empty ledger totals to 0. A one-line sum(...) over a generator expression does it.
total_by_category(ledger)
A dict mapping each category to the sum of its amounts -> the classic accumulate-into-a-dict loop. Start with {} and use totals.get(category, 0) to default a new category to zero before adding to it.
total_by_category(ledger)
# {"food": 3.5, "transport": 2.0}top_category(ledger)
The single category with the highest total. Build on total_by_category, then reach for max(d, key=d.get). For an empty ledger there is no top category -> return the empty string "".
Press Run to grade. The hidden tests build a small ledger and check every value exactly. Get all four green and you have a tested data layer -> Part 2 turns it into an app you can actually talk to.
Write four pure functions over a ledger (a list of expense dicts with keys description, amount, category). add_expense(ledger, desc, amount, category) returns a NEW ledger with one appended dict and must not mutate the input. total(ledger) returns the summed amount (0 when empty). total_by_category(ledger) returns a dict of category -> summed amount ({} when empty). top_category(ledger) returns the category with the largest total, or "" when empty.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.