The Deep Selector
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L4
- Asked in
- W. W. Grainger
Problem
A service hands back deeply nested JSON config, and downstream code needs only a handful of values from it, each named by a dot-separated path like user.name. Given data (the nested dict) and fields (the list of paths), return a dict mapping each path to the value found by walking that sequence of dict keys. A path maps to None when any segment along the way is missing or lands on something that is not a dict.
Tell it what you want. It knows where to look.
Example 1
Input:
data = {"user":{"age":30,"name":"Alice"},"address":{"city":"NY"}}, fields = ["user.name","address.city","user.email"]Output:
{"user.name":"Alice","address.city":"NY","user.email":null}Example 2
Input:
data = {"meta":{"id":"x1"},"order":{"items":{"count":3}}}, fields = ["order.items.count","meta.id","order.items.price"]Output:
{"order.items.count":3,"meta.id":"x1","order.items.price":null}Worked solution and explanation
What this really is
This is safe nested traversal wearing a JSON-extractor costume. The skill being probed: can you walk a variable-length key sequence into a structure of unknown depth without blowing up when the structure runs out before the path does? Splitting on dots and indexing is the easy 80 percent. The candidates who fail are the ones who assume every intermediate is a dict and write current[key] blindly. The first time a path reaches one level deeper than the data goes, say user.name.first where name is the string 'Bob', that line raises TypeError and takes the whole batch down with it.
Missing is normal, not exceptional
Notice the visible example asks for user.email, which simply is not there, and the expected answer is None, not a dropped key or an error. Partial resolution is the common case in config and API payloads, so the function must treat a missing or dead-ends-early path as a first-class, ordinary outcome. That reframes the whole problem: you are not looking up keys, you are attempting a walk that may fail at any step, and a failed walk yields None.
How to get there
Step 1: Split each path on dots
'order.items.count' becomes the segment sequence ['order', 'items', 'count']. Each segment is the next dict key to descend through.
Step 2: Attempt the walk, guarding each hop
Start at data and step inward one segment at a time. Before every step, confirm the thing in your hand is still a dict that actually holds the next key. The moment that check fails, stop: this path does not resolve.
Step 3: Record the outcome
If the walk consumed every segment, store the value you landed on. Otherwise store None. Either way the original dot-path stays the key.
The solution
def extract_json_fields(data: dict, fields: list[str]) -> dict:
result = {}
for path in fields:
current = data
resolved = True
for key in path.split('.'):
if isinstance(current, dict) and key in current:
current = current[key]
else:
resolved = False
break
result[path] = current if resolved else None
return resultThe isinstance check is what lets a single-key path return a whole sub-dict and a too-deep path return None, both without raising.
Common follow-up questions
- What if a path could index into lists, like `order.items[0].sku`? (Tests parsing mixed dot and bracket notation and casting indices to int.)
- How would you write a value at a dot-separated path, creating missing levels? (Tests creating intermediate dicts on the way down rather than reading.)
- If thousands of paths share long common prefixes, how would you avoid re-walking them? (Tests prefix sharing via a trie or memoization across many overlapping paths.)