The Online Elite
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
- L4
- Asked in
- Visa
Problem
A payments platform logs every transaction as online or offline and ties each to a merchant. Given the merchants and the payments, return the average of all online transaction amounts belonging to the top_n merchants by total online volume, or 0.0 when no online transactions qualify.
The top performers are hiding in the data.
Example
Input:
merchants = [{"name":"ShopA","merchant_id":1},{"name":"ShopB","merchant_id":2},{"name":"ShopC","merchant_id":3}], payments = [{"mode":"online","amount":100,"merchant_id":1},{"mode":"online","amount":200,"merchant_id":1},{"mode":"online","amount":50,"merchant_id":2},{"mode":"offline","amount":50,"merchant_id":2},{"mode":"online","amount":400,"merchant_id":3}], top_n = 2Output:
233.33333333333334
Worked solution and explanation
What this problem really is
Underneath the merchant-analytics story this is a two-grain aggregation, and the grain switch is the whole game. You total online amounts per merchant to decide who the top_n are, then you throw those totals away and average the raw online transactions of only those merchants. A plain group-by hands you the totals; the trap is finishing the job with them. Average the per-merchant totals (350) instead of the individual payments (233.33) and you ship a number that looks reasonable and is quietly wrong, and forget the empty case and the platform 500s on a ZeroDivisionError the first quiet day with no online sales.
The two grains
Hold both grains at once. The merchant-level total is scaffolding: it exists only to rank and select. The value you return lives at the transaction level, scoped to the merchants that survived the cut. Mix them up and every requirement after step 3 goes wrong.
Step 1: Filter to online transactions
Keep only payments where mode == 'online'. Everything downstream reads from this filtered list, so offline rows never touch the total or the average.
Step 2: Sum online volume per merchant
Each payment already carries its merchant_id, so a single pass into a dict keyed by that id gives the per-merchant online total in O(t). Record the index where each merchant first appears so ties can be resolved later.
Step 3: Rank and cut to top_n
Sort the merchant ids by a (-total, first_seen) tuple key so the biggest totals come first and equal totals keep the order their online payments first appeared, then slice the first top_n. The explicit first-appearance term keeps ties from drifting to id order.
Step 4: Average the transactions of the survivors
Scan the online transactions once more, keep those whose merchant is in the top set, and return the mean of their amounts (0.0 if the set is empty).
The solution
def top_online_merchant_avg(merchants, payments, top_n):
online = [p for p in payments if p.get('mode') == 'online']
volume = {}
first_seen = {}
for i, p in enumerate(online):
mid = p['merchant_id']
if mid in volume:
volume[mid] += p['amount']
else:
volume[mid] = p['amount']
first_seen[mid] = i
ranked = sorted(volume, key=lambda mid: (-volume[mid], first_seen[mid]))
top_ids = set(ranked[:top_n])
amounts = [p['amount'] for p in online if p['merchant_id'] in top_ids]
if amounts:
return sum(amounts) / len(amounts)
else:
return 0.0Common follow-up questions
- How would you express this in SQL instead of Python? (Tests window functions, CTEs, and GROUP BY, and whether they can express the two-grain average in one query.)
- What if transactions had timestamps and you needed the top merchants per month? (Tests adding a time-based grouping dimension to the ranking.)
- How would you handle payments missing the `merchant_id` or amount field? (Tests defensive filtering and data-quality handling.)
- If the business wanted ties broken by highest single transaction instead of input order, how would that change the sort? (Tests whether they can make the tie-break explicit and deterministic under a stated business rule.)