Against the Grain
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
- 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.
Turn the grid on its side and let every column stand up as a row.
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
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 resultThe outer index runs over input columns, so each pass builds exactly one output row.
- 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.)