Every Line on the Receipt
A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- medium
- Seniority
- L5
- Asked in
- Walmart
Problem
Point-of-sale transaction events arrive nested: each event has a store_id, a timestamp, and an items list of line items with an item_name and item_price. Flatten events into one record per line item that carries its parent's store_id and timestamp, keeping the order the events and their items arrived in.
Nested deep inside the receipt. Pull every item out.
Example 1
Input:
events = [{"items":[{"item_name":"milk","item_price":3.99},{"item_name":"bread","item_price":2.49}],"store_id":"S001","timestamp":"2024-01-15T10:30:00"},{"items":[{"item_name":"eggs","item_price":4.29}],"store_id":"S002","timestamp":"2024-01-15T11:00:00"}]Output:
[{"store_id":"S001","item_name":"milk","timestamp":"2024-01-15T10:30:00","item_price":3.99},{"store_id":"S001","item_name":"bread","timestamp":"2024-01-15T10:30:00","item_price":2.49},{"store_id":"S002","item_name":"eggs","timestamp":"2024-01-15T11:00:00","item_price":4.29}]Example 2
Input:
events = [{"items":[],"store_id":"S003","timestamp":"2024-01-16T09:00:00"},{"items":[{"item_name":"soda","item_price":1.5}],"store_id":"S004","timestamp":"2024-01-16T09:05:00"}]Output:
[{"store_id":"S004","item_name":"soda","timestamp":"2024-01-16T09:05:00","item_price":1.5}]Worked solution and explanation
Why this problem exists in real interviews
This is a parent-child explode wearing a receipt costume. Retail point-of-sale data almost never arrives in the shape analytics needs: a transaction is one header (store, timestamp) wrapping a list of line items, and the row count you owe the warehouse is the total number of items, not the number of transactions. The real skill being probed is whether you can carry the header fields DOWN onto every child while nested-iterating, and whether you trust an empty inner list to disappear on its own. Anyone can write the two loops. The tell is what you do with a voided transaction: reach for an if not items branch and you corrupt the fact table.
Break down the requirements
Step 1: Walk the events in input order
The outer structure is a list of transaction dicts. Iterate it as given so the emitted rows stay deterministic; nothing here should sort or regroup.
Step 2: Explode each event's items
For each event, iterate its items list in its listed order. Each item becomes exactly one output record, so the inner loop is where rows are actually born.
Step 3: Carry the header onto every line
Read store_id and timestamp once per event, then stamp them onto each item's record alongside item_name and item_price. Every output dict has exactly these four keys.
Step 4: Let empty lists drop out by construction
An event with an empty items list runs the inner loop zero times and contributes nothing; an empty events list yields an empty result. No special-case branch, and never a placeholder or None-filled row.
The solution
def flatten_transactions(events):
rows = []
for event in events:
store_id = event['store_id']
timestamp = event['timestamp']
for item in event['items']:
rows.append({
'store_id': store_id,
'item_name': item['item_name'],
'timestamp': timestamp,
'item_price': item['item_price'],
})
return rowsOuter loop pins the header, inner loop emits one row per item.
Follow-up questions an interviewer may ask
Common follow-up questions
- How would this change if events streamed in one at a time and could not all fit in memory? (Tests whether they reach for a generator that yields rows lazily instead of building the full list.)
- What if some events were missing the `store_id` or `items` key entirely? (Probes defensive access with `.get` and defaults, and the decision to skip versus raise.)
- How would you parallelize this across a large batch, and where would input order be preserved or lost? (Checks understanding of partitioning and where ordering guarantees survive a distributed explode.)