# Hidden Depths

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

Canonical URL: <https://datadriven.io/problems/hidden_depths>

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.

## 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**

```python
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
```

> **Cost**
>
> **Time:** O(n * d) where n is the number of keys and d is the average number of dot segments per key, since each key is split once and walked once.
> 
> **Space:** O(n * d) for the nested dict that gets built. No recursion, so no call-stack depth to worry about even for deeply dotted keys.

> **Interviewers watch for**
>
> The tell of seniority here is iterating `parts[:-1]` (or `range(len(parts) - 1)`) and handling the last segment separately. Candidates who loop over all segments and try to special-case the value assignment inside the loop usually produce an off-by-one that nests the value under a stray empty dict.

> **Common pitfall**
>
> Conflicting keys. If `'a.b' = 1` arrives before `'a.b.c' = 2`, the cursor hits an int where it expected a dict and `'b' not in current` is False, so `current = current['b']` hands you an int and the next assignment throws. State up front whether inputs are conflict-free, or decide explicitly between overwrite and raise. Silently swallowing it is the real trap.

---

## Common follow-up questions

- How would you handle array indices in the path, like 'a.0.b'? _(Tests creating lists at numeric segments instead of dicts.)_
- How would you flatten a nested dict back into dot-keyed form? _(Tests recursive traversal with path accumulation, the inverse transform.)_
- What if a key segment could itself contain an escaped dot? _(Tests splitting on unescaped dots only, which needs real parsing rather than a naive split.)_

## Related

- [All practice problems](https://datadriven.io/problems)
- [Mock interview mode](https://datadriven.io/interview/hidden_depths)
- [Python Interview Questions](https://datadriven.io/python-interview-questions)
- [Data Engineering Interview Prep Guide](https://datadriven.io/data-engineer-interview-prep)
- [Daily Challenge](https://datadriven.io/daily)

---

Source: DataDriven (https://datadriven.io). DataDriven is the data engineering interview community. Live code execution in SQL, Python, and Spark sandboxes. Every feature is open to every member.