How Lodash flattenDeep Recursively Flattens Arrays
Lodash’s _.flattenDeep is a popular utility function
designed to recursively unpack arbitrarily nested arrays into a single,
one-dimensional array. This article explores the internal mechanics of
_.flattenDeep, focusing on how it leverages Lodash's
internal baseFlatten helper, manages recursive call paths,
verifies flattenable elements, and ensures complete array flattening
with optimal execution.
The Entry Point:
flattenDeep
In the Lodash source code, _.flattenDeep acts as a
specialized wrapper. It does not implement the traversal logic directly;
instead, it delegates the heavy lifting to an internal utility function
called baseFlatten.
The function signature for flattenDeep is defined
as:
function flattenDeep(array) {
const length = array == null ? 0 : array.length;
return length ? baseFlatten(array, INFINITY) : [];
}Here, INFINITY is defined as 1 / 0 (or
JavaScript's Infinity). Passing infinite depth indicates to
the core engine that nesting should be resolved completely, regardless
of how many layers deep the arrays are nested.
The Core Engine:
baseFlatten
The recursive mechanism lives inside baseFlatten. This
internal function accepts several parameters:
array: The array currently being processed.depth: The maximum recursion level allowed (which isInfinityforflattenDeep).predicate: A function determining if an item should be flattened (defaults to an internalisFlattenablecheck).isStrict: A boolean flag restricting non-flattenable elements from being preserved (used by other Lodash functions).result: The accumulator array where flattened values are stored.
When baseFlatten runs, it allocates an accumulator array
(result) if one is not passed. It then iterates
sequentially through the input array using a standard
for...of or index-based while loop.
The Recursive Branch vs. Base Values
During iteration, each element of the array is evaluated against the
predicate function isFlattenable. In Lodash, an element is
considered flattenable if it is a standard array, an
arguments object, or an object with a
Symbol.isConcatSpreadable property set to
true.
The recursive branching logic functions as follows:
- Depth Check and Type Evaluation: If
depth > 0and the current value passesisFlattenable, the function branches into recursion. - Recursive Execution:
baseFlattencalls itself recursively, passing the nested array, a decremented depth (depth - 1), the predicate, and the sharedresultaccumulator:BecausebaseFlatten(value, depth - 1, predicate, isStrict, result);Infinity - 1is stillInfinity, the depth constraint never restrictsflattenDeepuntil no nested arrays remain. - Accumulation (Base Case): If an element is not
flattenable or the depth has reached
0, the element is appended to theresultarray:result[result.length] = value;
By passing the same mutable result array through every
recursive frame, Lodash avoids the performance penalties associated with
creating intermediate arrays and concatenating them (such as using
Array.prototype.concat).
Conceptual Vanilla JavaScript Implementation
To illustrate how _.flattenDeep achieves this, the
following conceptual implementation mirrors Lodash’s internal recursion
model:
function isFlattenable(value) {
return Array.isArray(value) || Boolean(value && value[Symbol.isConcatSpreadable]);
}
function baseFlatten(array, depth, result = []) {
for (let i = 0; i < array.length; i++) {
const value = array[i];
if (depth > 0 && isFlattenable(value)) {
// Recurse into the nested array while passing the accumulated result
baseFlatten(value, depth - 1, result);
} else {
// Base case: push non-array value directly to the result
result.push(value);
}
}
return result;
}
function customFlattenDeep(array) {
return array && array.length ? baseFlatten(array, Infinity, []) : [];
}Call Stack and Recursion Limits
Because _.flattenDeep relies on recursive function
calls, the depth of the nesting directly correlates to the call stack
depth in the JavaScript engine. If an array is nested deeper than the
engine's maximum call stack size (typically between 10,000 and 25,000
recursive frames depending on the browser or runtime), the function will
throw a RangeError: Maximum call stack size exceeded. For
typical web development use cases, nested depths rarely exceed double
digits, making Lodash's combined loop-and-recursion pattern both fast
and memory-efficient.