The Stream Joiner
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
Problem
You're correlating two time-sorted event streams where each event is a dict carrying a timestamp and a value, given a numeric tolerance. Walking stream_a in order, pair each event with the single nearest event in stream_b whose timestamp lies within tolerance of it, emitting a dict of a_value, b_value, and the absolute gap; an A event with nothing inside its window contributes no row. When two B events are equally near, keep the earlier one, and remember a single B event can be the nearest match for several A events.
Events don't wait for each other. This does.
Example
Input:
stream_a = [{"value":"a1","timestamp":1},{"value":"a2","timestamp":5},{"value":"a3","timestamp":12}], stream_b = [{"value":"b1","timestamp":2},{"value":"b2","timestamp":4.5},{"value":"b3","timestamp":20}], tolerance = 3Output:
[{"a_value":"a1","b_value":"b1","gap":1},{"a_value":"a2","b_value":"b2","gap":0.5}]Worked solution and explanation
What this really is
Strip away the streaming language and this is a nearest-neighbor search between two sorted sequences. Anyone can pair every A with every B and keep the closest, and it even passes the tests. The thing that separates candidates is exploiting the sort: you never need to rescan B from the start. A shared pointer that only crawls forward turns an O(a*b) double loop into a single linear pass. Get that pointer wrong and you either rescan everything, or worse, advance it past B events that a later A event still needs.
Break down the requirements
Step 1: Iterate through stream A events
For each event in A, find the closest match in B within the allowed time difference and emit a dict with a_value, b_value, and gap.
Step 2: Use a pointer into B to skip events that are too early
Since both streams are sorted, once a B event is earlier than a_time minus tolerance it is too early for this A and for every later A, so the left pointer can advance past it permanently.
Step 3: Track the closest match within the tolerance
Among all B events inside the window [a_time - tolerance, a_time + tolerance], pick the one with the smallest absolute time difference; ties go to the earliest, which the forward scan hits first. Break out once a B event is past a_time + tolerance.
Step 4: Emit the joined dicts
If a match was found, append the dict with gap as a float; otherwise skip that A event. B events are never consumed, so one B can match several A events.
The solution
def windowed_join(stream_a: list, stream_b: list, tolerance: float) -> list:
matches = []
left = 0
for a_event in stream_a:
a_time = a_event["timestamp"]
# Advance the shared pointer past B events too early for this (and every later) A.
while left < len(stream_b) and stream_b[left]["timestamp"] < a_time - tolerance:
left += 1
best_b = None
best_gap = float("inf")
j = left
while j < len(stream_b):
b_time = stream_b[j]["timestamp"]
if b_time > a_time + tolerance:
break
gap = abs(a_time - b_time)
if gap < best_gap:
best_gap = gap
best_b = stream_b[j]
j += 1
if best_b is not None:
matches.append({
"a_value": a_event["value"],
"b_value": best_b["value"],
"gap": float(best_gap),
})
return matchesCommon follow-up questions
- What if one B event should only be matched to one A event? (Tests greedy vs. optimal matching and potentially Hungarian algorithm for global optimum.)
- How would you handle streams arriving out of order? (Tests buffering and re-sorting within a watermark window.)
- What if tolerance varies per event type? (Tests parameterizing the window lookup per event.)
- How would this work in a distributed system with multiple stream partitions? (Tests partitioning strategy and cross-partition join semantics.)