Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
JavaScriptDevelopmentTutorial

How to flatten an array in JavaScript: flat(), flatMap() and deep nesting

Flatten nested JavaScript arrays with flat(), flat(Infinity) and flatMap(), see why flat(Infinity) and recursion fail on very deep input, and use a tested iterative version that does not.

Cloudsoft Zone

Updated 5 min read

A set of painted matryoshka nesting dolls taken apart and lined up from largest to smallest, with the Cloudsoft Zone logo in the corner
In this article

To flatten an array in JavaScript, choose between flat() with a depth, JavaScript flatMap() to map and flatten in one step, and hand-written recursive, iterative or lazy versions. This JavaScript flatten array guide also covers deep nesting, speed and common mistakes.

To flatten an array in JavaScript, use array.flat() for one level, array.flat(depth) for a known depth and array.flat(Infinity) for any depth. To transform and flatten in one step, use flatMap(). These built-ins cover nearly every case. The exceptions are extremely deep nesting, where flat(Infinity) runs out of stack, and old one-liners such as reduce with concat, which are much slower than they look. This guide shows each method with results, measures them, and gives a version that handles any depth.

Tested with Node.js 24.19.0 (V8 13.6). Every result shown in a comment was printed by running the code; the same code runs in current Chrome, Firefox and Safari.

flat() and flatMap() arrived in ES2019 and work in every current browser and in Node.js 11 or later, so you no longer need a polyfill or Lodash's _.flatten and _.flattenDeep for this. Many older answers use recursion with forEach and Array.isArray; the hand-written versions below show when that is still worth doing.

flat(): one level, a set depth, or every level

JavaScript
const nested = [1, [2, [3, [4]]]];
console.log(nested.flat());         // [ 1, 2, [ 3, [ 4 ] ] ]  (one level by default)
console.log(nested.flat(2));        // [ 1, 2, 3, [ 4 ] ]
console.log(nested.flat(Infinity)); // [ 1, 2, 3, 4 ]
console.log([1, , 3, [4, , 6]].flat()); // [ 1, 3, 4, 6 ]  (holes are removed)

const orders = [{ id: 1, items: ['pen', 'ink'] }, { id: 2, items: ['pad'] }];
console.log(orders.flatMap((order) => order.items)); // [ 'pen', 'ink', 'pad' ]
console.log([1, 2, 3].flatMap((n) => (n % 2 ? [n, n * 10] : []))); // [ 1, 10, 3, 30 ]

flat() returns a new array and leaves the original unchanged. Note the fourth line: it removes holes in sparse arrays, so the result can be shorter than you expect. It only flattens real arrays; strings, typed arrays, Sets and array-like objects are kept as single values.

flatMap(): map and flatten one level

flatMap(fn) is the same as map(fn).flat() but makes one pass. It is the cleanest way to turn each item into zero, one or several items, as the last example above does by returning an empty array to drop even numbers. It only flattens one level; if your callback returns nested arrays, call flat() on the result.

Writing your own: recursive, iterative and lazy

You rarely need a hand-written version, but it is a common interview question and there is one real reason to have one: depth. All three functions below give the same result as flat(Infinity):

JavaScript
// Recursive: short and clear, but every level of nesting uses a stack frame.
export function flattenDeep(array) {
  const out = [];
  for (const item of array) {
    if (Array.isArray(item)) out.push(...flattenDeep(item));
    else out.push(item);
  }
  return out;
}

// Iterative: an explicit stack, so any depth works and order is kept.
export function flattenIterative(array) {
  const out = [];
  const stack = [[array, 0]];
  while (stack.length) {
    const top = stack[stack.length - 1];
    const [current, index] = top;
    if (index === current.length) { stack.pop(); continue; }
    top[1]++;
    const item = current[index];
    if (Array.isArray(item)) stack.push([item, 0]);
    else out.push(item);
  }
  return out;
}

// Lazy: yields values one at a time, useful for very large inputs.
export function* flattenLazy(array) {
  for (const item of array) {
    if (Array.isArray(item)) yield* flattenLazy(item);
    else yield item;
  }
}

Deep nesting: where flat(Infinity) breaks

We built an array nested 100,000 levels deep and flattened it with each method:

JavaScript
let deep = [0];
for (let i = 1; i <= 100_000; i++) deep = [deep, i];

deep.flat(Infinity);     // RangeError: Maximum call stack size exceeded
flattenDeep(deep);       // RangeError: Maximum call stack size exceeded
flattenIterative(deep);  // 100001 items

V8 implements flat() recursively, so it fails at the same kind of depth as a recursive function. Ordinary data, such as API responses or a tree of menu items, is never nested anywhere near that deeply. But if the depth comes from user input, such as a pasted JSON document or a generated tree, use the iterative version so a hostile input cannot crash the process.

Which method is fastest?

We flattened 1,000 arrays of 100 two-item arrays (200,000 numbers in total), 20 runs each, on Node.js 24.19.0 on an Apple M2 laptop:

MethodTime per run
flattenIterative2.5 ms
flat(2)3.1 ms
flat(Infinity)3.4 ms
flattenDeep (recursive)4.3 ms
Generator, spread into an array22 ms
reduce((acc, x) => acc.concat(x), []), one level only67 ms

The built-in and the plain loops are all within a few milliseconds of each other, so choose flat() for readability. Avoid the reduce plus concat pattern still found in many older answers: concat copies the whole accumulator on every step, so the work grows with the square of the input, and it flattened only one level while taking twenty times longer. If you need reduce, push into the accumulator instead of concatenating. The generator is slower but uses almost no extra memory if you consume it with for...of instead of spreading it.

Common mistakes

  • Expecting flat() to flatten everything. The default depth is 1. Pass Infinity when nesting is unknown.
  • Using toString() or join() and splitting. [1, [2, [3]]].toString().split(',') returns strings, not numbers, and breaks on values containing commas and on objects.
  • Spreading into concat with huge arrays. [].concat(...arrays) passes every inner array as an argument and can exceed the engine's argument limit on very large inputs; arrays.flat() has no such limit.
  • Confusing arrays with objects. Flattening a nested object into dotted keys, such as { "a.b": 1 }, is a different task that needs its own function.

Once arrays are flat, comparing them is the next common task; see how to compare arrays in JavaScript correctly.

Frequently asked questions

  • How do I flatten a deeply nested array in JavaScript?

    Use array.flat(Infinity). For input that might be nested tens of thousands of levels deep, such as untrusted JSON, use an iterative function with an explicit stack, because flat(Infinity) and recursion throw a stack overflow.

  • What is the difference between flat() and flatMap()?

    flat(depth) only flattens. flatMap(fn) calls fn on each item and then flattens the result by exactly one level, in a single pass.

  • Does flat() change the original array?

    No. flat() and flatMap() return a new array. They do remove holes from sparse arrays.

  • Which browsers support Array.prototype.flat()?

    All current browsers and Node.js 11 or later. It has been part of the language since ECMAScript 2019.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. Array.prototype.flat() — MDN Web Docs
  2. Array.prototype.flatMap() — MDN Web Docs
  3. Array.prototype.concat() — MDN Web Docs
  4. yield* — MDN Web Docs

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.