The request

From Megan Collins, Head of Sales
Your goal
Step 1 of 3. Set result to a list of the order_id values of orders whose status is 'completed', in their original order.
- Store a list of 4 in
resultnot done yet
Step 2 of 3. Set result to a dictionary mapping each completed order's order_id to its total (sum of quantity times unit_price over its items), each total rounded to 2 decimals.
- Store a dict in
resultnot done yet
Step 3 of 3. Set result to a dictionary mapping each channel to the total revenue of its completed orders, rounded to 2 decimals. Only channels with at least one completed order appear.
- Store a dict in
resultnot done yet
Step 1 · your code
Loading the editor…
Reference solution
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 2 · your code
Loading the editor…
Reference solution
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
Step 3 · your code
Loading the editor…
Reference solution
Mote checks your result
Press Run. Each row that matches the goal becomes a firefly in your lantern.
What Megan gets, and why
The lesson
Nested data
Real exports are rarely flat. Here each order is a dictionary, and one of its values, items, is
itself a list of (quantity, unit_price) pairs. That’s one order with many lines, the same
relationship that orders and order_items have in the Harbor & Pine database.
To work with nested data, peel one layer at a time:
order = orders[0]
order["items"] # [(2, 49.99), (1, 34.0)]
order["items"][0] # (2, 49.99)
qty, price = order["items"][0] # unpack the pair
A pair in round brackets is a tuple: like a list, but fixed once created. Tuples are common for small groups of values that belong together.
Step 1: filter
You did this in the previous level. A comprehension with a condition keeps the IDs of completed orders:
[o["order_id"] for o in orders if o["status"] == "completed"]
Cancelled, refunded and pending orders aren’t revenue. Deciding which statuses count is a business decision; always state it in your answer so nobody compares your number with one that includes refunds.
Step 2: total each order
The total of one order is the sum of its lines. sum() accepts a generator expression, a
comprehension without the square brackets, so you don’t need to build a temporary list:
sum(qty * price for qty, price in order["items"])
Wrap that in a dictionary comprehension and you get a lookup from order ID to total, for every completed order at once.
Step 3: group and sum
Revenue by channel is the counting pattern from earlier, except you add the order total instead of 1:
revenue = {}
for o in orders:
if o["status"] != "completed":
continue
total = sum(qty * price for qty, price in o["items"])
revenue[o["channel"]] = revenue.get(o["channel"], 0) + total
continue skips straight to the next order. It’s a tidy way to throw out rows early instead of
nesting the rest of the body inside an if.
Round at the very end, once per channel. Then check: the channel totals should add up to the sum of the order totals from step 2. Cross-checking two numbers you computed in different ways is one of the cheapest and most effective habits an analyst can have.
What you just did
Filter rows, compute a value per row, group by a category and sum. That’s the shape of a huge share of business reporting. In chapter 6 you’ll do the same thing on 30,000 order lines with pandas in two lines of code, and you’ll know exactly what those two lines are doing.
Common mistakes
- Summing the unit prices without multiplying by quantity.
- Forgetting to filter out cancelled and refunded orders in step 3.
- Rounding each order before adding it to the channel, which can shift the total by a cent.
Lantern lit!
Practised: list comprehensiondictionariesnested dataaccumulator
- Solved
- No hints
- First run
