Flattening Nested Arrays
Key Learning Objectives
Implement recursive array flattening with customizable depth parameters.
Implement iterative array flattening using an explicit stack to prevent call stack overflow.
Master native Array.prototype.flat() semantics: default depth of 1, Infinity for complete flattening.
Understand sparse array handling: why flat() removes uninitialized empty slots (holes).
Analyze time and space complexity tradeoffs across recursive and iterative flattening algorithms.
The Interview Problem
What is logged to the console when Array.prototype.flat() is executed on deeply nested and sparse arrays, and how does the native flattening algorithm handle uninitialized empty slots?
1const nested = [1, [2, [3, [4]]]];2const res1 = nested.flat(1);3const res2 = nested.flat(Infinity);45const sparse = [1, , 3];6const res3 = sparse.flat();78console.log(9 res1.length,10 res2.join('-'),11 res3.length,12 1 in sparse13);
Predict Console Output
Select the option that matches what standard ECMAScript prints to the console:
3 1-2-3-4 2 false
4 1-2-3-4 3 true
3 1-2-3-4 3 false
2 1-2-3-4 2 true
V8 Engine Execution Trace
Step 1 of 6 (Line 1)Allocates nested array hierarchy in heap memory.
Deep Technical Breakdown
Implementing Array Flattening in JavaScript
Flattening arrays is a standard FAANG interview problem with two principal implementation patterns:
1. Recursive Implementation with Depth Control
function flatten(arr, depth = 1) {
if (depth <= 0) return arr.slice();
const result = [];
for (const item of arr) {
if (Array.isArray(item)) {
result.push(...flatten(item, depth - 1));
} else {
result.push(item);
}
}
return result;
}- Time Complexity: $O(N)$ where $N$ is the total count of elements across all nested arrays.
- Space Complexity: $O(D)$ where $D$ is the maximum recursion depth, plus $O(N)$ for the output array.
- Limitation: Extremely deep recursion (e.g., $D > 10,000$) will trigger
RangeError: Maximum call stack size exceeded.
2. Iterative Implementation Using an Explicit Stack
To eliminate call stack limitations, manage state using an in-memory stack:
function flattenIterative(arr) {
const stack = [...arr];
const result = [];
while (stack.length > 0) {
const next = stack.pop();
if (Array.isArray(next)) {
stack.push(...next);
} else {
result.push(next);
}
}
return result.reverse();
}The Sparse Array Trap
A sparse array contains indices that have never been initialized ([1, , 3]). This is fundamentally different from [1, undefined, 3]:
1 in [1, , 3]is false (no property key'1'exists on the array).1 in [1, undefined, 3]is true.- The ECMAScript specification (§ 23.1.3.11) mandates that
Array.prototype.flatdiscards empty slots entirely.[1, , 3].flat()produces[1, 3].
Common Traps & Mistakes
Assuming `flat()` defaults to flattening all levels. Its default depth is `1`. To flatten completely, pass `Infinity`.
Using naive recursion without depth control or tail call optimization on untrusted deeply nested inputs.
Confusing unallocated sparse array holes (`[1, , 2]`) with `undefined` values (`[1, undefined, 2]`).
Using `toString()` or `arr.join(',')` to flatten numeric arrays. This destroys nested empty arrays (`[]`), alters strings with commas, and converts types.
FAANG Follow-Up Probes
Probe #1
How does Array.prototype.flatMap() differ from calling .map() followed by .flat() in terms of performance and depth?
Probe #2
How would you implement a generator-based lazy flattener (function* flatGen(arr)) to stream elements without loading the entire flattened array in memory?
Probe #3
How does V8 optimize array iteration when encountering packed arrays versus holey (sparse) arrays?
