The Messy Pipeline
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
- L4
- Asked in
- eHealth
Problem
Our upstream sensor export arrives as a data list that mixes plain numbers, the same numbers written as strings, None gaps, and the occasional batch of readings wrapped in its own inner list. Turn it into one flat list of every reading scaled by multiplier, kept in the order the readings arrive.
The upstream API has no idea what a schema is.
Example
Input:
data = [1,"2",[3,4],null,"5"], multiplier = 2
Output:
[2,4,6,8,10]
Worked solution and explanation
What this really is
This is a type-dispatch problem dressed up as a data cleanup. The skill being probed: can you look at each item and decide, before you touch it, whether it is a gap, a batch, or a scalar? A one-liner float(x) * multiplier over the raw list blows up on the first None or inner list, so the dispatch is not optional. Everyone gets the multiply right. What separates candidates is what they quietly lose: filter to int and float and every reading that arrived as '2' disappears with no error, turn None into 0.0 and you invent measurements nobody took, or sort the result to make it look tidy and the readings no longer line up with the order the export sent them.
Walking it through
Step 1: Drop the gaps first
None means no reading was taken, so it leaves the stream entirely. Checking item is None first also keeps it away from float(), which would raise on it.
Step 2: Open the batches one level
An inner list is a batch, not a value. isinstance(item, list) routes it into a second loop that scales each reading inside it. One level is all the export produces, so there is no recursion to build.
Step 3: Coerce everything else with one call
Everything left is a number or a numeric string, and float() accepts both. That is why there is no separate string branch: the conversion already handles '5' and 5 identically, and it guarantees a float even when the input was an int.
Step 4: Return in the order you met them
Appending as you walk preserves arrival order for free. There is nothing to reorder at the end, so the function returns scaled exactly as it was built.
def coerce_and_flatten(data: list, multiplier: float) -> list[float]:
scaled = []
for item in data:
if item is None:
continue
if isinstance(item, list):
for reading in item:
scaled.append(float(reading) * multiplier)
else:
scaled.append(float(item) * multiplier)
return scaledOne pass over data; each inner batch is opened exactly once.
- Filter to numbers
if isinstance(item, (int, float))keeps 1 and drops '2' and '5'. The output for the example is three values instead of five, and nothing raises to tell you.- Coerce what you can
- Skip
None, open lists, and hand everything else tofloat(). The numeric strings become readings, and the output has all five values.
Common follow-up questions
- What if some strings are not numeric, like 'n/a', and should be skipped? (Tests wrapping `float()` in `try`/`except ValueError` and deciding what a bad value means.)
- What if batches can nest arbitrarily deep? (Tests recursive flattening or an explicit stack.)
- How would you process an export with millions of readings without holding them all in memory? (Tests turning the function into a generator that yields readings instead of building a list.)