# The Roll Call

> Every value is waiting for its name.

Canonical URL: <https://datadriven.io/problems/the_roll_call>

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.

## 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.

**Header-to-row zipping into dicts**

```python
def rows_to_dicts(headers, rows):
    return [dict(zip(headers, row)) for row in rows]
```

> **Trick to solving**
>
> `dict(zip(headers, row))` is the entire answer. `zip` pairs by position, `dict` collapses the pairs into a record, and the comprehension repeats it per row. No index arithmetic means no off-by-one to get wrong.

> **Common pitfall**
>
> The earlier instinct is to sort the keys for a tidy field order. Resist it. Dict equality ignores key order, so the sort buys nothing the grader can see and adds an O(c log c) pass per row. Shipping a needless sort on a hot transform is the kind of detail an interviewer notices.

> **In production...**
>
> `zip` stops at the shorter sequence. If a row is short, you silently drop trailing columns instead of raising; if it is long, the extra values vanish. The prompt promises aligned lengths, so this is correct here, but in production you would guard the lengths or reach for `itertools.zip_longest` to surface the bad row.

**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.

> **Performance insight**
>
> Time is O(n * c) for n rows of c columns, which is optimal since every cell must be visited once. Space is O(n * c) for the output. Removing the per-row key sort drops the wasted O(c log c) factor the naive version carried.

---

## 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.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/the_roll_call)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). DataDriven is the data engineering interview community. Live code execution in SQL, Python, and Spark sandboxes. Every feature is open to every member.