The Shortest Path Home

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
L3
Asked in
Amazon

Problem

API responses arrive as dicts nested arbitrarily deep, and the warehouse needs them as single-level rows. Collapse nested into a flat dict whose keys are the full path down to each leaf, joined by separator, where any value that is not itself a dict is a leaf. An empty inner dict contributes no key, so that branch simply disappears from the result.

Every leaf remembers the road it came from. Bring them all back on one line.

Example 1

Input:

nested = {"user":{"name":"Ada","address":{"zip":"75001","city":"Paris"}},"active":true}, separator = "_"

Output:

{"user_name":"Ada","user_address_zip":"75001","user_address_city":"Paris","active":true}

Example 2

Input:

nested = {"id":7,"meta":{}}, separator = "_"

Output:

{"id":7}

Worked solution and explanation

What this really is

This is recursion carrying a breadcrumb. The skill being probed: can you remember the path you walked down so far and stitch it onto each leaf, without dribbling a separator onto the front of every root key? Everyone writes the recursive descent. The candidates who stumble are the ones who concatenate prefix, separator, and key unconditionally and hand back keys like '_user_name' with a stray leading underscore on every top-level entry.


Break down the requirements

Step 1: Recurse with an accumulating prefix

Thread a prefix string through a recursive helper. When you descend into a sub dict, append the current key to the prefix using the separator. The recursion bottoms out the moment a value is not itself a dict: that value is a leaf and gets written under the full joined path.

Step 2: Respect the separator parameter

Default to '_' per the spec, but never hard code it inside the recursion. Threading the separator through the helper keeps the function reusable for dotted '.' notation or any custom delimiter, which is exactly the kind of reuse an interviewer probes with a follow-up.

Step 3: Handle the empty prefix correctly

On the first call there is no prefix yet, so the joined key is just the current key, not '_foo'. The guard f'{prefix}{separator}{key}' if prefix else key produces clean root keys and avoids leaking a leading separator into every output key.


The solution

Recursive walker with key path accumulator
def flatten_json(nested: dict, separator: str = '_') -> dict:
    flat = {}

    def _walk(obj, prefix):
        for key, value in obj.items():
            new_key = f'{prefix}{separator}{key}' if prefix else key
            if isinstance(value, dict):
                _walk(value, new_key)
            else:
                flat[new_key] = value

    _walk(nested, '')
    return flat

Common follow-up questions