# Against the Grain

> Turn the grid on its side and let every column stand up as a row.

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

Domain: Python · Difficulty: medium · Seniority: L4

## Problem

A metrics export comes back as a `grid` of equal-length rows, one row per day and one column per metric. Reorient it so each metric's column becomes its own row, and return an empty list when `grid` has no rows or its rows are empty.

## Example 1

Input:

```
grid = [[1,2,3],[4,5,6]]
```

Output:

```
[[1,4],[2,5],[3,6]]
```

## Example 2

Input:

```
grid = [[1,2],[3,4],[5,6]]
```

Output:

```
[[1,3,5],[2,4,6]]
```

## Worked solution and explanation

### What this really is

Strip the spreadsheet costume and this is a coordinate swap: the cell sitting at row i, column j has to land at row j, column i. The whole problem turns on one realization that trips people up: the output's row index comes from the input's *column* index. Candidates who try to walk the input row by row end up either overwriting cells or building the wrong shape. The ones who get it iterate over columns on the outside and rows on the inside, and the code falls out cleanly.

---

### Working through it

#### Step 1: Get the dimensions straight first

An m x n input (m days, n metrics) becomes an n x m output (n metrics, m days). Pin those dimensions down before writing a single loop, because that is the contract the result has to satisfy.

#### Step 2: Build one output row per input column

Drive the outer loop over columns of the input. Each column is one metric's run of values across every day, and that is exactly one row of the output. Reading a fixed column down every input row is the move that does the swap.

#### Step 3: Handle the empty shapes

Before indexing grid[0], guard the empty cases. An empty grid and a grid of empty rows both have nothing to reorient, so both return an empty list.

---

### The solution

**Outer loop over columns**

```python
def transpose(grid: list) -> list:
    if not grid or not grid[0]:
        return []
    rows = len(grid)
    cols = len(grid[0])
    result = []
    for c in range(cols):
        new_row = []
        for r in range(rows):
            new_row.append(grid[r][c])
        result.append(new_row)
    return result
```

*The outer index runs over input columns, so each pass builds exactly one output row.*

> **Cost**
>
> Time is O(m * n): every cell is touched once. Space is O(m * n) for the new grid. There is no faster option, since you have to read every element to place it.

> **Interviewers watch for**
>
> The tell is which dimension the outer loop runs over. Looping over input rows on the outside is the instinctive move and the wrong one here; looping over columns is what signals you understood the coordinate swap rather than memorized a snippet.

> **Common pitfall**
>
> zip(*grid) is the slick one-liner, but it yields tuples, not lists. If the contract wants lists of lists (and the tests here do), bare zip fails silently on type. Wrap it: [list(col) for col in zip(*grid)]. The explicit loop sidesteps the trap entirely.

**Loop over rows (wrong instinct)**

Walking the input row by row appends whole input rows back, leaving the orientation unchanged or the indices tangled.

**Loop over columns (correct)**

Walking column by column collects each metric's values down the grid, producing one clean output row per metric.

---

## Common follow-up questions

- How would you do this in place for a square grid? _(Tests swapping grid[i][j] with grid[j][i] for j > i without an extra buffer.)_
- What changes if the rows can have different lengths? _(Tests padding shorter rows with a fill value or rejecting the input.)_
- How does numpy handle this, and why is it nearly free there? _(Tests knowledge of array.T and its zero-copy view semantics.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/against_the_grain)
- [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.