Hidden Depths

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

Problem

A config loader returns settings as a flat dict whose keys encode their depth with dots, like a.b.c for a value three levels down. Return the equivalent nested dict, where each dot-separated segment of a key becomes one level deeper and the value lands at the end of its path; a key with no dots stays at the top level.

Each dot marks a level. Rebuild the tree it stands for.

Example 1

Input:

flat = {"d":3,"a.b":1,"a.c":2}

Output:

{"a":{"b":1,"c":2},"d":3}

Example 2

Input:

flat = {"x.y.z":10}

Output:

{"x":{"y":{"z":10}}}

Worked solution and explanation

What this really is

Strip off the config-management costume and this is path insertion into a tree built out of plain dicts. Everyone splits the key on dots. What separates candidates is the walk: for every segment except the last you descend one level, minting an empty dict the first time you touch a level, and only the final segment receives the value. Treat the last segment like the rest and you bury the value one level too deep under an empty dict; skip the minting and you crash on the second key that shares a prefix.


Break it down

Step 1: Split each key into path segments

'database.host.port' becomes ['database', 'host', 'port']. A key with no dot becomes a single-element list, which is why dotless keys naturally land at the top level with no special case.

Step 2: Walk to the parent, creating levels on demand

Hold a cursor at the result dict. For each segment except the last, look for a child dict there: create one if it is missing, then move the cursor into it. The 'create if missing' is what lets a later key like 'a.c' reuse the 'a' dict that 'a.b' already built.

Step 3: Drop the value at the last segment

Once the cursor sits at the deepest existing parent, assign the value under the final segment. This is the single line that must be outside the descend loop.


The solution

Iterative path walking
def unflatten(flat: dict) -> dict:
    result = {}
    for key, value in flat.items():
        parts = key.split(".")
        current = result
        for segment in parts[:-1]:
            if segment not in current:
                current[segment] = {}
            current = current[segment]
        current[parts[-1]] = value
    return result

Common follow-up questions