A pricing desk receives quotes one at a time as (timestamp, code, value) records, where code names an instrument. A downstream model wants a table instead: one row per timestamp, with one column per instrument it cares about, columns sorted alphabetically by code.
Implement class RowTransformer:
RowTransformer(codes: list[str]): the registered codes (duplicates in the list count once). Records for any other code are ignored completely.columns() -> list[str]: the registered codes in sorted order.process(timestamp: int, code: str, value: float) -> list | None: feed one record. If it completes a row, return that row; otherwise returnNone.flush() -> list | None: the stream is pausing; return the row that is still being built (orNoneif there isn't one).
A row is [timestamp, v_1, v_2, ..., v_C], with the values in columns() order. (In C++ and Java a row is a list of nullable doubles, so the timestamp comes back as a double in slot 0. Timestamps fit in 32 bits, so this is exact.)
Rules:
- Records mostly arrive in non-decreasing timestamp order, grouped by timestamp; the exception is late records (below). The row for timestamp
tis open from its first record until a record with a larger timestamp arrives (that call returns the finished row fortand opens the next one) or untilflush()returns it. - A code with several records at the same timestamp takes the last value.
- A code with no record at timestamp
tcarries forward its most recent value from an earlier timestamp, orNoneif it has never had one. - A late record, whose timestamp is below the open row's timestamp or, when no row is open, not above the last returned row's timestamp, is ignored and
processreturnsNone. - After
flush(), the next record with a larger timestamp opens a new row as usual (and returnsNone, since nothing was open).
rt = RowTransformer(["MSFT", "AAPL", "IBM"])
rt.columns() # ["AAPL", "IBM", "MSFT"]
rt.process(1, "MSFT", 410.0) # None
rt.process(1, "AAPL", 190.5) # None
rt.process(1, "TSLA", 250.0) # None (not registered)
rt.process(2, "AAPL", 191.0) # [1, 190.5, None, 410.0]
rt.process(2, "AAPL", 191.25) # None (replaces 191.0)
rt.process(1, "IBM", 170.0) # None (late, ignored)
rt.process(4, "IBM", 171.0) # [2, 191.25, None, 410.0]
rt.flush() # [4, 191.25, 171.0, 410.0]
rt.flush() # None
Constraints: up to 10,000 codes and 200,000 records. Each process call must be O(1) on average except for building a finished row, which is O(C). Don't search or sort the code list per record.
Show hint
Map each registered code to its column index once, in the constructor. Keep one list of "latest value per column"; a finished row is just the timestamp plus a copy of that list.