Twice on the Wire
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
- junior
- Asked in
- Visa
Problem
A card reader retransmits its last status ping whenever an ACK is dropped, so the raw stream arrives with the same status repeated back to back. Collapse each run of consecutive identical pings into a single entry, keeping the surviving pings in their original order.
A dropped ACK makes the reader say it again. Keep the signal, drop the echo.
Example 1
Input:
pings = ["online","online","degraded","online"]
Output:
["online","degraded","online"]
Example 2
Input:
pings = ["online","offline","online"]
Output:
["online","offline","online"]
Worked solution and explanation
Why this problem exists in real interviews
This is run-length collapse wearing a payments costume, and the whole thing turns on one word: consecutive. The skill being probed is whether you compare each ping to its PREVIOUS neighbor, not to a bag of everything seen so far. Almost everyone hears 'remove the duplicates' and reaches for set(pings) or dict.fromkeys(pings). That erases every repeat globally, so the last 'online' in the first example, a genuine event that arrived after a 'degraded', silently disappears. Get it wrong and your collapsed stream is missing real authorizations and the count no longer matches reality.
Break down the requirements
Step 1: Compare against the previous ping, not a seen-set
The rule is local: drop a ping only when it equals the one immediately before it. That is fundamentally different from global dedup. Keep the value you last emitted and skip while the next value matches it.
Step 2: Preserve order and non-adjacent repeats
A status that comes back later after something else in between is a brand-new event, so it must survive. The output is the sequence of run leaders in arrival order. Never sort, never hash the whole stream.
Step 3: Let empty and single-element inputs fall out for free
An empty stream returns an empty list and a one-element stream returns itself. A clean run-collapse handles both with no special branch, so resist adding guard clauses that only add noise.
The solution
from itertools import groupby
def collapse_pings(pings):
return [status for status, _ in groupby(pings)]- Global dedup (wrong)
- list(dict.fromkeys(pings)) keeps first-seen order but removes EVERY later repeat. On ['online','online','degraded','online'] it returns ['online','degraded'], deleting the final real 'online'.
- Consecutive collapse (right)
- [k for k, _ in groupby(pings)] only merges adjacent equal values. Same input returns ['online','degraded','online'], preserving the genuine later event.
Common follow-up questions
- What if pings were dicts like {'status': 'online', 'ts': 1234} and you collapse on the status field only? (Tests groupby(pings, key=lambda p: p['status']) and deciding which record in the run to keep.)
- How would you also return how many raw pings each surviving entry absorbed? (Tests [(k, len(list(g))) for k, g in groupby(pings)] and awareness that the group iterator is consumed once.)
- The stream is now infinite and arrives one ping at a time. How do you collapse it online? (Tests a stateful generator that remembers only the last emitted value, no full-list buffering.)