Against the Current
A hard Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- hard
- Seniority
- L5
- Asked in
- Deloitte
Problem
A nightly export hands you a CSV log far too large to fit in memory, delivered as an iterable that yields one line at a time with the header row first. Given a column_name, return the sum of that column's numeric values as a float. Rows whose value in that column is missing or does not parse as a number are skipped, and if column_name is not among the header cells the sum is 0.0.
Too big to load. Read what you can.
Example 1
Input:
lines = ["a,b,c","1,2,3","4,5,6"], column_name = "b"
Output:
7
Example 2
Input:
lines = ["amount,note","1.5,ok","xx,bad","2.25,ok"], column_name = "amount"
Output:
3.75
Worked solution and explanation
What this problem is really about
This looks like a column sum, but it is really a test of whether you can resist the one instinct that crashes the job. Almost everyone reaches for list(lines) or pandas.read_csv() to get random access to the rows, and on a multi-gigabyte export that line is an OOM kill before a single number is added. The whole problem is staying lazy: csv.reader pulls one line at a time, so the trick is feeding lines straight into it and never holding more than a running total in memory.
Working through it
Step 1: Wrap the iterable in a streaming reader
Hand lines to csv.reader so it consumes the iterable lazily, row by row. The moment you bind it to a list comprehension or call list(), you have lost the only thing this problem grades.
Step 2: Resolve the column index once
Pull the first row as the header and locate the target with header.index(column_name). If it raises ValueError, the column does not exist and there is nothing to sum, so return 0.0 right away. Guard the empty-input case too: next(reader) on an exhausted iterator raises StopIteration.
Step 3: Convert defensively and accumulate
For each remaining row, grab the value at the column index and convert it. A short row, a blank cell, or a non-numeric string should silently skip, not abort the stream, so wrap the float() in a try/except and only add on success.
The solution
import csv
def sum_column(lines, column_name):
reader = csv.reader(lines)
try:
header = next(reader)
except StopIteration:
return 0.0
try:
idx = header.index(column_name)
except ValueError:
return 0.0
total = 0.0
for row in reader:
if idx >= len(row):
continue
try:
total += float(row[idx])
except (ValueError, TypeError):
continue
return totalCommon follow-up questions
- What if the lines come from a gzip-compressed file? (Tests wrapping a file with gzip.open and passing the resulting line iterator in.)
- How would you process the input in parallel? (Tests splitting by byte offset or using multiprocessing.Pool with chunk boundaries.)
- What if the CSV uses a non-comma delimiter? (Tests passing `delimiter` to `csv.reader`.)
- How would you compute both sum and count in a single pass? (Tests maintaining two accumulators in the same loop.)