Skip to main content

Command Palette

Search for a command to run...

Array Flatten in JavaScript

Updated
6 min readView as Markdown

If you’ve ever opened a kitchen drawer only to find containers tucked inside other containers, you’ve encountered a "nested" structure. In JavaScript, we deal with the same thing in our data.

In this guide, we’ll learn how to take those messy, layered arrays and turn them into a clean, single line of data.

What are nested arrays?

An array in JavaScript is like a shelf that holds items. But what if one of those items is itself another shelf? That's a nested array — an array living inside another array.

Here's what one looks like in code:

// A simple flat array
const flat = [1, 2, 3];

// A nested array — arrays inside arrays
const nested = [1, [2, 3], [4, [5, 6]]];

The diagram below shows what [1, [2, 3], [4, [5, 6]]] actually looks like — boxes within boxes, each level representing a deeper layer of nesting.

The key term here is depth — how many layers of brackets you have to open to reach an actual value. Depth 0 is the outermost array. Depth 1 is one level in. Depth 2 is two levels deep, and so on.

Why flattening arrays is useful

Real-world data is often structured in nested groups. Consider these everyday scenarios:

  • API response: You fetch orders from a server. Each order contains a list of items. You end up with [[item, item], [item], [item, item, item]] — but you just want one list of all items.

  • Search results: You query three categories and each returns an array of results. Merging them gives you nested arrays, but you need one flat list to display.

  • Matrix operations: A 2D grid (rows of columns) must be processed as a single sequence of values.

Flattening converts messy nested structures into a clean, linear array you can loop over, filter, sort, or display directly — without writing extra loops for every level of nesting.

The concept of flattening

Flattening means removing inner array brackets and pulling all values up into the outermost array. Think of it like taking every item out of every nested box and placing them all on a single tray — in order, left to right, depth first.

"Flatten = remove the brackets, keep the values, preserve the order."

The key variable is depth — how many levels of nesting you want to remove:

Different approaches to flatten arrays

Method 1 — Array.flat() — the modern way

Introduced in ES2019, .flat() is the cleanest, most readable approach. It accepts a depth argument (default is 1).

const nested = [1, [2, 3], [4, [5, 6]]];

nested.flat();           // [1, 2, 3, [4, [5, 6]]]  → depth 1
nested.flat(2);          // [1, 2, 3, 4, [5, 6]]    → depth 2
nested.flat(Infinity);   // [1, 2, 3, 4, 5, 6]      → all levels

How it works step by step:

  1. JavaScript visits each element of the outer array in order.

  2. If the element is itself an array, it "unpacks" it — copying its contents into the parent array.

  3. It repeats this unpacking process down to the depth you specified.

  4. Values that are not arrays are copied as-is

Method 2 — flatMap() — transform and flatten

flatMap() is like doing .map() and .flat(1) in a single, efficient pass. Use it when you want to transform items and the result of each transformation is itself an array.

const sentences = ["hello world", "foo bar"];

// map alone gives a nested result
sentences.map(s => s.split(" "));
// [["hello", "world"], ["foo", "bar"]]  ← nested!

// flatMap flattens one level automatically
sentences.flatMap(s => s.split(" "));
// ["hello", "world", "foo", "bar"]  ← flat!

Method 3 — reduce() + concat() — the manual way

Before .flat() existed (pre-2019), developers used this pattern. It's valuable to understand because it reveals how flattening actually works under the hood.

const nested = [1, [2, 3], [4, 5]];

const flat = nested.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4, 5]

Step by step what reduce does here:

  1. Start with an empty accumulator[].

  2. Visit1[].concat(1)[1].

  3. Visit[2,3][1].concat([2,3])[1, 2, 3]. (concatspreads arrays automatically.)

  4. Visit[4,5][1,2,3].concat([4,5])[1, 2, 3, 4, 5].

    Limitation: reduce + concat only flattens one level deep. For deeper nesting you'd need to make it recursive (see Method 4).

Method 4 — Recursion — full depth control

When nesting depth is unknown and you want complete control, write a recursive function. This is the most important approach to know for interviews.

function flattenDeep(arr) {
  return arr.reduce((acc, val) => {
    if (Array.isArray(val)) {
      return acc.concat(flattenDeep(val)); // recurse deeper
    } else {
      return acc.concat(val);              // it's a plain value
    }
  }, []);
}

flattenDeep([1, [2, [3, [4]]]]);
// [1, 2, 3, 4]

Quick comparison

Method Depth Control Transform Items Works without ES2019
.flat(n) Yes No No
.flatMap() Depth 1 only Yes No
reduce + concat Depth 1 only Customizable Yes
Recursion Any depth Customizable Yes

Common interview scenarios

Array flattening is a favourite interview topic because it tests recursion, array methods, and problem-solving thinking all at once. Here are the patterns you'll see most often.

"Flatten without using .flat()"

Write the recursive function from scratch. Key insight: check Array.isArray(el) on each item and recurse into it.

"Flatten to a specific depth"

Add a depth parameter and decrement it on each recursive call. Stop recursing when depth === 0

"What does .flat() return?"

A new array — it does not mutate the original. Always mention immutability when discussing array methods in interviews.

"Handle unknown nesting depth"

Use .flat(Infinity) or recursive function. Mention you'd verify depth is not insanely large to avoid stack overflow in production.

Interview pattern 1 — Flatten without .flat()

function flatten(arr) {
  let result = [];
  for (const item of arr) {
    if (Array.isArray(item)) {
      result = result.concat(flatten(item)); // recurse
    } else {
      result.push(item);
    }
  }
  return result;
}

Interview pattern 2 — Flatten to a given depth

function flattenToDepth(arr, depth = 1) {
  return arr.reduce((acc, val) => {
    if (Array.isArray(val) && depth > 0) {
      return acc.concat(flattenToDepth(val, depth - 1)); // go one level deeper
    }
    return acc.concat(val);
  }, []);
}

flattenToDepth([1, [2, [3]]], 1); // [1, 2, [3]]
flattenToDepth([1, [2, [3]]], 2); // [1, 2, 3]

Interview mindset: Before writing code, always ask — "Do I know the depth? Do I need to transform values? Am I allowed to use built-in methods?" Interviewers reward candidates who think before they type.

Key takeaways — at a glance