The Weight of the Book

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
Addepar

Problem

An asset-allocation report needs each asset class placed in order of how much money sits in it, where several holdings can carry the same asset_class and their market values add up. Assign each class a position starting at 1 for the class holding the most total value, and when two classes hold the same total, give the alphabetically earlier name the stronger position. An empty book has nothing to place.

Every holding pulls its weight. Order the asset classes by what they carry, and let the ties settle by name.

Example 1

Input:

holdings = [{"asset_class":"equity","market_value":5000},{"asset_class":"fixed_income","market_value":3000},{"asset_class":"equity","market_value":2000},{"asset_class":"cash","market_value":1000}]

Output:

{"cash":3,"equity":1,"fixed_income":2}

Example 2

Input:

holdings = [{"asset_class":"commodities","market_value":2000},{"asset_class":"cash","market_value":2000},{"asset_class":"real_estate","market_value":5000}]

Output:

{"cash":2,"commodities":3,"real_estate":1}

Worked solution and explanation

Why this problem exists in real interviews

This is a group-then-order problem wearing a portfolio report's costume, and the whole thing balances on one line: the sort key. Anyone can sum market values into a dict keyed by asset class. What separates candidates is the tie-break, because the ordering runs in two directions at once: total value from highest to lowest, but equal totals from A to Z. Reach for a plain reverse=True on the totals and you flip the names too, so every tie comes out backwards. That is exactly the row the interviewer plants to see whether you actually thought about ties or just eyeballed the happy path.


Break down the requirements

Step 1: Fold holdings into per-class totals

Several holdings share an asset class, so you cannot order the raw records. Walk the list once and accumulate into a dict with totals.get(cls, 0) + value. This is the step a plain sort of the input can never do for you, and it is why the answer needs a dict at all.

Step 2: Order the classes with one compound key

Sort the class names by (-total, name). The negative total gives you largest-first without reverse=True, and the trailing name breaks ties alphabetically in ascending order. Doing this as two separate concerns is where people slip.

Step 3: Turn the order into a position map

enumerate(ordered, start=1) hands you position 1, 2, 3 in order. Return a dict of class to position rather than a list, so the grade checks the actual positions you assigned, not just which classes showed up.


The solution

Aggregate, order by compound key, map to positions
def rank_asset_classes(holdings):
    totals = {}
    for h in holdings:
        totals[h["asset_class"]] = totals.get(h["asset_class"], 0) + h["market_value"]
    ordered = sorted(totals, key=lambda cls: (-totals[cls], cls))
    return {cls: rank for rank, cls in enumerate(ordered, start=1)}
reverse=True (breaks ties)
sorted(totals, key=lambda c: totals[c], reverse=True) puts the biggest book first, but reverse flips the entire comparison. Two classes tied at 2000 come out in Z-to-A order, so cash lands behind commodities instead of ahead of it.
compound key (correct)
sorted(totals, key=lambda c: (-totals[c], c)) negates only the total. The name still sorts ascending, so tied classes settle cash before commodities. Both rules live in one key with no flag to fight.

Common follow-up questions