The Progress Meter
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
- L3
- Asked in
- Stitch Fix
Problem
A long-running batch job logs a cumulative reading after each item it finishes, and we want a fixed ten-point progress summary no matter how many readings a run produced. Given the readings in order, return the value standing at each ten-percent mark through the list, from the 10% mark up to the 100% mark.
Ten glimpses of a run, no matter how long it ran.
Example 1
Input:
readings = [3,8,15,22,30,41,55,60,72,80,88,95,103,110,118,125,130,142,150,160]
Output:
[8,22,41,60,80,95,110,125,142,160]
Example 2
Input:
readings = [100,250,400]
Output:
[100,100,100,250,250,250,400,400,400,400]
Worked solution and explanation
What this problem really is
This is quantile sampling wearing a progress-bar costume. The real question: can you drop ten evenly spaced sample points onto a list whose length you don't control? Everyone reaches for readings[::step] with step = len(readings) // 10, and it betrays them the moment the length isn't a clean multiple of ten. The slice yields the wrong count, drifts off the last element, and divides by zero when the list is shorter than ten. The move that works is to compute each mark's index straight from the length with a ceiling division, so the 100% mark always lands on the last reading and short runs repeat values instead of crashing.
Why the obvious approach breaks
- Fixed-step slice (wrong)
- step = len(readings) // 10; readings[step-1::step]. For a length of 3 the step is 0 and you crash. For a length of 25 the step is 2 and you get 12 or 13 points, not 10, and the last one is nowhere near 100%.
- Per-mark index (right)
- For each mark k in 1..10, compute the index (k * n - 1) // 10. The count is pinned at ten by construction, the k=10 mark is always the final element, and n < 10 simply repeats indices.
Step 1: Turn each mark into an index
The k-th ten-percent mark is at position ceil(k * n / 10), counted from one. Convert to a zero-based index by subtracting one: ceil(k*n/10) - 1 is exactly (k*n - 1) // 10, so a single integer expression gives every mark with no floats and no rounding drift.
Step 2: Collect the ten samples
Walk k from 1 to 10 and read readings at that index. Ten iterations, independent of input length, so the answer is always ten values in mark order.
The solution
def progress_milestones(readings: list) -> list:
n = len(readings)
# ceil(k * n / 10) - 1: index of the k-th 10% mark, rounded up, zero-based
return [readings[(k * n - 1) // 10] for k in range(1, 11)]Common follow-up questions
- What if the caller wants an arbitrary number of evenly spaced checkpoints, not just ten? (Tests generalizing the fixed decile count to an arbitrary parameter.)
- What if a mark should return the interpolated value between two readings instead of snapping to one? (Tests interpolation between readings rather than nearest-index sampling.)
- How would you emit these marks live if the run length is unknown until it finishes? (Tests streaming: emitting milestones as readings arrive without knowing the total length up front.)