The Roll Call
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L3
Problem
A CSV reader hands you the column names in headers and the table body in rows, where each row is a list of values positionally aligned to those headers. Return one dict per row that maps each column name to that row's value.
Every value is waiting for its name.
Example
Input:
rows = [["Alice","30","Boston"],["Bob","25","NYC"]], headers = ["name","age","city"]
Output:
[{"age":"30","city":"Boston","name":"Alice"},{"age":"25","city":"NYC","name":"Bob"}]Worked solution and explanation
What this really is
This is positional alignment wearing a data-parsing costume. The whole problem is gluing two parallel sequences together by index: the header at position i names the value at position i, for every row. Almost everyone reaches for a manual range(len(headers)) index loop, and it works on the happy path. The tell of a stronger candidate is recognizing that Python already has the exact primitive for walking two lists in lockstep, so they never write an index variable at all. Reach for indices here and you also hand-roll the off-by-one risk that the primitive eliminates for free.
The move
Step 1: Pair, don't index
zip(headers, row) yields (header, value) pairs in order, so you never touch an index. Feed those pairs straight into dict(...) and you have one record. This is the single insight the problem is probing.
Step 2: Map it across every row
Wrap that per-row expression in a comprehension over rows. One flat, readable line builds the whole list of records, and an empty rows naturally yields an empty list with no special case.
def rows_to_dicts(headers, rows):
return [dict(zip(headers, row)) for row in rows]- Manual indexing
- for i in range(len(headers)): record[headers[i]] = row[i] Same compute, but you introduce an index variable, a mutable dict, and an off-by-one surface for no benefit.
- zip
- dict(zip(headers, row)) No index, no manual mutation, reads as exactly what it does: pair the columns with the values.
Common follow-up questions
- What if some rows are shorter than headers and you must not silently drop columns? (Tests defensive coding: detect the mismatch or pad with `zip_longest`.)
- How would you reverse this, turning the dicts back into headers plus rows? (Tests pulling keys and values back into parallel lists.)
- What changes if the dataset is too large to hold the full output list in memory? (Tests a generator-based, streaming approach for memory.)