The Month-by-Month Snapshot

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
JPMorgan Chase

Problem

An employee often logs several sales in the same month, so each month in our sales crosstab must hold that employee's combined total, keyed by the month's lowercased three-letter abbreviation. From the records, return one dict per employee with those month totals plus their employee_id, ordered alphabetically by employee_id.

Every salesperson has a story. The months just tell it sideways.

Example

Input:

records = [{"month":"January","employee_id":"E2","sales_amount":7000},{"month":"January","employee_id":"E1","sales_amount":5000},{"month":"February","employee_id":"E1","sales_amount":3000}]

Output:

[{"jan":5000,"feb":3000,"employee_id":"E1"},{"jan":7000,"employee_id":"E2"}]

Worked solution and explanation

This is a pivot with a duplicate-key trap dressed up as a sales report. Anyone can turn 'January' into jan. What separates candidates is whether each employee-month cell accumulates or overwrites. Write row[key] = record['sales_amount'] and the last record wins: an employee with two March sales of 1000 and 2500 reports 2500 instead of 3500. Nothing errors, and the report quietly loses real revenue.

Building it

Step 1: Key the totals by employee, then by month

The output has one dict per employee, so the natural working state is a dict of dicts: totals[employee_id][month_key]. setdefault(emp, {}) hands back the employee's inner dict, creating it on first sight, so the loop body never branches on whether the employee is new.

Step 2: Derive the month key from the data

The column names are not fixed in advance; they come from the values. record['month'][:3].lower() maps 'January' to 'jan' and 'September' to 'sep'. Keying on the full name would pass a quick eyeball check and fail every graded case.

Step 3: Accumulate, never assign

months.get(key, 0) + amount treats a missing month as zero and adds to an existing one. This one line is the whole difference between a correct crosstab and one that keeps only the last sale of each month.

Step 4: Order employees, not months

The records arrive in export order, and in the example E2 shows up before E1. sorted(totals) walks employees alphabetically by employee_id. Inside each dict only the keys and totals matter, so the months need no sort; employee_id is added after them to keep each row readable.

Dict-of-dicts crosstab with data-derived month keys
def monthly_sales_crosstab(records: list[dict]) -> list[dict]:
    totals: dict[str, dict[str, int]] = {}
    for record in records:
        emp = record['employee_id']
        key = record['month'][:3].lower()
        amount = record['sales_amount']
        months = totals.setdefault(emp, {})
        months[key] = months.get(key, 0) + amount
    result = []
    for emp in sorted(totals):
        row = totals[emp]
        row['employee_id'] = emp
        result.append(row)
    return result
Overwrite
months[key] = amount keeps whichever record came last. M3's two March sales (1000, then 2500) collapse to 2500, and the 1000 vanishes without a trace.
Accumulate
months[key] = months.get(key, 0) + amount folds every record into its cell. M3's March reads 3500, and a month with a single sale is unaffected.

Common follow-up questions