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 = 6

Output:

{"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

Index, filter, aggregate
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