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:

  1. array: The array currently being processed.
  2. depth: The maximum recursion level allowed (which is Infinity for flattenDeep).
  3. predicate: A function determining if an item should be flattened (defaults to an internal isFlattenable check).
  4. isStrict: A boolean flag restricting non-flattenable elements from being preserved (used by other Lodash functions).
  5. 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:

  1. Depth Check and Type Evaluation: If depth > 0 and the current value passes isFlattenable, the function branches into recursion.
  2. Recursive Execution: baseFlatten calls itself recursively, passing the nested array, a decremented depth (depth - 1), the predicate, and the shared result accumulator:
    baseFlatten(value, depth - 1, predicate, isStrict, result);
    Because Infinity - 1 is still Infinity, the depth constraint never restricts flattenDeep until no nested arrays remain.
  3. Accumulation (Base Case): If an element is not flattenable or the depth has reached 0, the element is appended to the result array:
    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.