Above the Title
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
You have three exports from a film catalog: movies (each with a movie_id, genre, rating, and box_office), actors (each with an actor_id and name), and mapping, the bridge rows that pair an actor_id with a movie_id. For a given genre and min_rating, return each actor's name mapped to the total box_office of the films they appeared in whose genre matches and whose rating is at least min_rating. Leave out any actor with no qualifying film.
Some names carry a genre. Total the box office behind them.
Example
Input:
genre = "Action", actors = [{"name":"Alice","actor_id":1},{"name":"Bob","actor_id":2}], movies = [{"genre":"Action","title":"ActionFlick","rating":7.5,"movie_id":1,"box_office":1000000},{"genre":"Comedy","title":"RomCom","rating":6,"movie_id":2,"box_office":500000},{"genre":"Action","title":"ActionFlick2","rating":5,"movie_id":3,"box_office":300000}], mapping = [{"actor_id":1,"movie_id":1},{"actor_id":1,"movie_id":2},{"actor_id":2,"movie_id":1},{"actor_id":2,"movie_id":3}], min_rating = 6Output:
{"Bob":1000000,"Alice":1000000}Worked solution and explanation
What this really is
Strip the movie costume and this is a filtered join across two dimension tables and a bridge, collapsed to one total per surviving actor. The thing being probed is whether you reach for lookup dicts the moment data arrives as flat lists, or whether you fall into the trap of scanning movies inside the actor loop. Anyone can get the right numbers; the people who get hired turn every repeated search into an O(1) dict read before they write the aggregation.
How to get there
Step 1: Index the actors
Build actors_by_id = {a['actor_id']: a['name'] for a in actors}. That one O(A) pass turns the bridge's actor_id into a name in constant time later, instead of re-scanning the actors list per mapping row.
Step 2: Filter the movies up front
Pre-compute eligible = {m['movie_id']: m['box_office'] for m in movies if m['genre'] == genre and m['rating'] >= min_rating}. Evaluating the predicate once per movie (not once per bridge row) is the difference between O(M) and O(M*K). Note >=: a rating exactly equal to min_rating qualifies.
Step 3: Aggregate in a single pass
Walk mapping once, skip any row whose movie_id is not in eligible, and add the stored box_office into a defaultdict(int) keyed by name. Casting to dict() at the end gives a plain dict and, crucially, never inserts a key for an actor who contributed nothing.
The solution
from collections import defaultdict
def actor_genre_box_office(movies: list[dict], actors: list[dict], mapping: list[dict], genre: str, min_rating: float) -> dict:
actors_by_id = {a['actor_id']: a['name'] for a in actors}
eligible = {
m['movie_id']: m['box_office']
for m in movies
if m['genre'] == genre and m['rating'] >= min_rating
}
totals = defaultdict(int)
for row in mapping:
movie_id = row['movie_id']
if movie_id not in eligible:
continue
actor_name = actors_by_id.get(row['actor_id'])
if actor_name is None:
continue
totals[actor_name] += eligible[movie_id]
return dict(totals)Common follow-up questions
- How would you return the result ordered by total `box_office`, highest first? (Wrap with dict(sorted(totals.items(), key=lambda kv: -kv[1])). Worth discussing that insertion order is preserved since Python 3.7, so the sorted order survives the cast.)
- What if two actors share the same name but have different ids? (Key the totals by `actor_id` and only translate to name in the final projection, otherwise two different people sharing a name get their revenue merged.)
- How would this look in pandas, and when is that the wrong tool? (Three DataFrames, two merge calls, then groupby('name')['`box_office`'].sum(). Compare readability and memory against the dict approach on a large catalog.)