All the Way Down

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 export arrives as a list whose entries are either plain values or further lists, nested to arbitrary depth with no fixed structure. Collapse it into a single flat list holding every leaf value in left-to-right order. Treat strings as leaf values, not as sequences to descend into.

However deep it nests, every leaf belongs on one line.

Example 1

Input:

nested = [1,[2,[3,4]],[5,[6,7]]]

Output:

[1,2,3,4,5,6,7]

Example 2

Input:

nested = ["a",["bc",["d"]]]

Output:

["a","bc","d"]

Worked solution and explanation

What this really is

Strip the costume off and this is a tree traversal wearing a list. Every value with brackets around it is an internal node; the numbers and strings you actually want are the leaves. Once you see it that way, the whole problem collapses to one decision made at every element: is this a branch I descend into, or a leaf I keep? The trap is the definition of 'leaf'. A string is iterable, so the lazy 'can I loop over it?' test walks straight into 'abc' and hands you back 'a', 'b', 'c'. That is the single mistake that separates a clean answer from a buggy one.


Building it

Step 1: One ordered pass

Walk the list once, in order, and ask the leaf-vs-branch question per element. Left-to-right iteration is what preserves output order for free; you never sort or reverse anything.

Step 2: Recurse and splice

When the element is a list, call yourself on it. The recursive call returns an already-flat list, so extend the accumulator with it. Using extend (not a nested append loop) keeps the splice in one readable line at the same cost.

Step 3: Collect leaves whole

Otherwise the element is a leaf: append it as-is. Numbers, strings, anything non-list lands here untouched.


The solution

Recursive flatten
def deep_flatten(nested):
    result = []
    for item in nested:
        if isinstance(item, list):
            result.extend(deep_flatten(item))
        else:
            result.append(item)
    return result

One list check per element; recursion handles arbitrary depth.

Naive iterable check
if hasattr(item, '__iter__'): recurse. Looks general, but strings are iterable, so 'bc' silently explodes into 'b', 'c' and the output is wrong in a way the simple test cases may not catch.
List check
if isinstance(item, list): recurse. Descends only into the one container the problem cares about. Strings, tuples, and dicts are all kept as single leaf values, which is exactly the contract.

Common follow-up questions