Katkat

Reason About Recursive Key Search in Nested JSON

MongoDBSoftware Engineer · Technical Screen

Last updated

Implement a recursive search function to extract all values associated with a given key from deeply nested JSON documents containing arbitrary combinations of dictionaries and lists.

Problem Statement

In document databases like MongoDB, documents can contain arbitrarily nested structures consisting of nested objects (maps/dictionaries) and arrays (lists).

Write a function find_values(document, target_key) that recursively traverses a nested JSON document and collects all values associated with target_key.

Your implementation must adhere to the following rules:

  • If an object contains target_key, append its associated value to the results. If that associated value is itself a nested dictionary or list, you should still search inside it for further occurrences of target_key.
  • If a list/array is encountered, recursively inspect each element in order.
  • If the target key appears multiple times across different branches or nesting depths, preserve pre-order depth-first traversal order in the returned list.
  • If the key is not present anywhere in the document, return an empty list [].
  • Keys are case-sensitive.

Function Signature

from typing import Any

def find_values(document: Any, target_key: str) -> list[Any]:
    pass

Examples

Example 1

input_doc = {
    "user": {
        "id": 101,
        "name": "Alice",
        "metadata": {
            "id": "sub_101",
            "status": "active"
        }
    },
    "tags": ["admin", "staff"],
    "id": 999
}
target = "id"

# Output:
# [101, "sub_101", 999] (or [999, 101, "sub_101"] depending on dict key ordering; pre-order traversal of keys)

Explanation: The key "id" appears at the root level (999), under "user" (101), and under "metadata" ("sub_101"). All three values are collected.

Example 2

input_doc = {
    "item": {
        "item": {
            "name": "Widget"
        }
    }
}
target = "item"

# Output:
# [{"item": {"name": "Widget"}}, {"name": "Widget"}]

Explanation: The outer "item" maps to a dict that itself contains another "item" key. Both values are captured.

Constraints

  • The document structure contains only valid JSON types: dict, list, str, int, float, bool, and None.
  • Nesting depth will not exceed 500 levels.
  • Total number of keys and array elements across the document is at most 10^5.

Edge Cases

  • The target value itself is None or an empty container ({} or []).
  • The root input is an empty dictionary, an empty list, or a primitive value.
  • Multiple identical values or duplicated keys across branches.

Hints

Hint 1

Identify your base cases first: primitive types (strings, numbers, booleans, and None) cannot be traversed further and should immediately return.

Hint 2

When traversing a dictionary, remember that finding a match does not mean you stop searching inside that branch if the matched value is itself a container.

Hint 3

Use a helper function that passes an accumulator list (or use a generator/yield pattern) to avoid repeated list concatenation overhead during recursion.

Follow-up questions

  • How would you modify this solution to return the full JSON path (e.g., 'user.metadata.id' or 'items[2].id') for each match?
  • If the document can contain circular references, how would you adapt your traversal to prevent infinite loops while still supporting JSON documents?
  • How would you rewrite this iteratively to avoid call-stack overflow in environments with strict recursion limits?