Footprints in the Feed

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

Problem

You're rolling up raw feed-handler log lines in lines, each a CSV row of date,process,host,log_message,bytes, where the process name carries the exchange as its leading token (so nyse_feed_handler is an NYSE process and nasdaq_market_data is a NASDAQ one). Roll the byte counts up two ways in one pass: total bytes per day, and total bytes per exchange within each day. Return both rollups together, the per-day totals first.

Every trade left a footprint.

Example 1

Input:

lines = ["2024-01-15,nyse_feed_handler,srv1,trade executed,1024","2024-01-15,nasdaq_market_data,srv2,quote update,2048","2024-01-15,nyse_order_router,srv1,order routed,512","2024-01-16,nasdaq_feed_handler,srv2,trade executed,4096"]

Output:

[{"2024-01-15":3584,"2024-01-16":4096},{"2024-01-15":{"nyse":1536,"nasdaq":2048},"2024-01-16":{"nasdaq":4096}}]

Example 2

Input:

lines = []

Output:

[{},{}]

Worked solution and explanation

What this problem really is

This is a two-dimensional rollup wearing a log-parsing costume. The parsing (split a comma row, grab the token before the first underscore) is the part everyone gets right. The skill actually being probed: can you accumulate a per-day total AND a per-exchange-within-day total in the SAME pass, where the second result is a dict of dicts you grow lazily as new (date, exchange) pairs show up. The trap is quietly expensive: candidates build one accumulator, or roll up only by day, then discover they have collapsed the exchange dimension into a single sum and cannot get it back. Once bytes from nyse and nasdaq land in the same per-day number, no later step can split them apart.


Break down the requirements

Step 1: Parse each CSV line into fields

Split on comma to get date, process, host, log_message, and bytes. Convert the bytes field with int(...). The host and log_message columns are noise for this task; you only touch positions 0, 1, and 4.

Step 2: Extract the exchange name from the process field

The exchange name is the portion before the first underscore in the process column: process.split('_')[0]. Note that a process with no underscore (like cme) returns the whole string unchanged, which is exactly the behavior you want.

Step 3: Accumulate bytes by day and by exchange-day

Keep two dicts side by side and update BOTH on every line, not one then the other in a second pass. daily_totals[date] accumulates a scalar; exchange_daily[date] is itself a dict that must exist before you index into it, so initialize it the first time you see a new date. Return them in a list in that exact order: [daily_totals, exchange_daily].


The solution

CSV split with dual-level aggregation
def aggregate_trades(lines: list) -> list:
    daily_totals = {}
    exchange_daily = {}
    for line in lines:
        parts = line.split(",")
        date = parts[0]
        process = parts[1]
        bytes_val = int(parts[4])
        exchange = process.split("_")[0]
        daily_totals[date] = daily_totals.get(date, 0) + bytes_val
        if date not in exchange_daily:
            exchange_daily[date] = {}
        exchange_daily[date][exchange] = exchange_daily[date].get(exchange, 0) + bytes_val
    return [daily_totals, exchange_daily]

One pass, both rollups updated together so the exchange dimension is never collapsed.


Common follow-up questions