What the Card Remembers
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
- mid
- Asked in
- Capital One
Problem
A card-authorization service records temporary holds as [start, end] minute offsets, and a single card can accumulate many holds that pile onto each other over a busy day. For each card, collapse the holds that overlap or touch into the continuous windows the money was actually held, earliest first.
A hold on your money is never a single moment. Rebuild the windows it actually lived in.
Example 1
Input:
holds = [{"end":30,"card":"visa","start":0},{"end":40,"card":"visa","start":20},{"end":10,"card":"amex","start":5},{"end":70,"card":"visa","start":60},{"end":25,"card":"amex","start":10}]Output:
{"amex":[[5,25]],"visa":[[0,40],[60,70]]}Example 2
Input:
holds = [{"end":55,"card":"visa","start":50},{"end":20,"card":"visa","start":10}]Output:
{"visa":[[10,20],[50,55]]}Worked solution and explanation
Why this problem exists in real interviews
This is interval merging wearing a payments costume. The real skill being probed: can you group records by a key, then collapse overlapping or touching ranges within each group in a single sorted sweep? Anyone can write the grouping dict. Two things separate candidates. First, holds arrive out of order, so a sweep in input order splits overlaps that a sort would have joined. Second, and the one that quietly fails: when a hold is fully contained inside the one before it, the running end must stay at the larger value. Take the new hold's end instead and a five-minute hold sitting inside a hundred-minute hold shrinks the window to five minutes.
Break down the requirements
Step 1: Partition holds by card before anything else
A global sort across all holds fuses windows from different cards the moment their offsets overlap. Build a dict from card to its list of (start, end) tuples with setdefault, so each card is merged in isolation. The grouping is the difference between one card's story and everyone's stories smeared together.
Step 2: Sort each card's holds by start
Overlapping holds can appear in any order in the input (see the visa example, where 50-55 is listed before 10-20). Sorting tuples orders by start, then end, which is exactly the sweep order you need. Skip the sort and two overlapping holds that arrive far apart stay separate.
Step 3: Sweep and extend, taking the max end
Walk the sorted holds. If the current hold's start is within (less than or equal to) the last merged window's end, they belong to one continuous window, so widen that window's end to max(existing_end, current_end). Using <= rather than < means a hold ending at 10 and one starting at 10 merge, which matches a continuous hold with no gap. Otherwise, open a new window.
The solution
def merge_holds(holds):
by_card = {}
for hold in holds:
by_card.setdefault(hold["card"], []).append((hold["start"], hold["end"]))
result = {}
for card, windows in by_card.items():
windows.sort()
merged = []
for start, end in windows:
if merged and start <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], end)
else:
merged.append([start, end])
result[card] = merged
return result- Naive: sweep in input order
- Iterate holds as given and compare each to the last window. Two overlapping holds listed far apart never meet, so one continuous hold is reported as two. On the visa example this splits 10-20 and 50-55 correctly by luck but mangles any interleaved overlap.
- Correct: sort within group, then sweep
- Group by card, sort each group by start so overlaps become adjacent, then a single linear sweep with a max-extended end collapses them exactly once. Deterministic and order-independent of the input.
Common follow-up questions
- The holds now arrive as an unbounded stream and you cannot hold a full card's history in memory. How do you emit merged windows? (Tests whether the candidate can keep only the open window per card and flush it once a hold starts strictly after the running end, trading the global sort for an assumption of near-ordered arrival.)
- Each hold also carries an amount. Report the total dollars held during each merged window. (Tests carrying an aggregate through the sweep and reasoning about whether overlapping amounts stack or the window represents a single held sum.)
- How would you run this across billions of holds in a distributed job? (Tests partitioning by card as the shuffle key so each reducer merges one card independently, mirroring the grouping step here.)