Skip to main content

Command Palette

Search for a command to run...

Array Flatten in JavaScript: Every Approach You Need to Know

Published
6 min read
Array Flatten in JavaScript: Every Approach You Need to Know
S
Full-stack developer obsessed with performance, scalability, and clean systems. I use Arch btw.

Introduction

If you've worked with data coming from an API or built any kind of recursive data structure, you've probably run into nested arrays. They show up everywhere. And at some point, you need to turn that messy structure into something flat and workable.

This post covers what nested arrays are, why you'd want to flatten them, and the different ways to do it in JavaScript, including what interviewers actually expect you to know.


What Are Nested Arrays?

A nested array is just an array that contains other arrays as elements.

// One level of nesting
const arr = [1, [2, 3], [4, 5]];

// Multiple levels
const deep = [1, [2, [3, [4, [5]]]]];

// Mixed nesting
const mixed = [1, [2, 3], [4, [5, 6]], 7];

Think of it like folders inside folders. The outer array is your root folder. Each inner array is a subfolder. Flattening means pulling everything out into one single folder.

// Before flattening
[1, [2, 3], [4, [5, 6]]]

// After flattening (one level)
[1, 2, 3, 4, [5, 6]]

// After full flattening
[1, 2, 3, 4, 5, 6]

Why Flattening Matters

The most common reason is data normalization. You get a response from an API where each item contains a list of sub-items, and you need one unified list to map over, filter, or pass into something else.

Other real scenarios:

  • Collecting results from multiple paginated responses into one array

  • Flattening a matrix (2D array) into a 1D array for computation

  • Processing tree-like structures like category hierarchies or comment threads

  • Simplifying recursive data before display

The practical upside is that flat arrays are easier to work with. Searching, sorting, and mapping over [1, 2, 3, 4] is a lot simpler than doing the same on [1, [2, [3, 4]]].


Approach 1: The Native flat() Method

JavaScript added Array.prototype.flat() in ES2019. It's the clearest option if you don't need to support old environments.

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

arr.flat();     // [1, 2, 3, 4, [5, 6]]  -- one level deep
arr.flat(2);    // [1, 2, 3, 4, 5, 6]   -- two levels deep
arr.flat(Infinity); // fully flattens regardless of depth

flat() takes a depth argument. The default is 1. Pass Infinity if you want to flatten everything no matter how deep the nesting goes.

Step by step for arr.flat():

  1. It iterates each element

  2. If the element is an array, it spreads its contents one level up

  3. Non-array elements stay as-is

Simple. Use this when you can.


Approach 2: reduce() + concat()

Before flat() existed, this was a common one-liner for shallow flattening:

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

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

What's happening here:

  • Start with an empty accumulator []

  • For each element, concat it onto the accumulator

  • concat automatically unwraps one level of array

This only works for one level of nesting. If val is [4, [5]], you get [1, 2, 3, 4, [5]], not [1, 2, 3, 4, 5].


Approach 3: Recursion

When you need to flatten arrays of arbitrary depth and can't use flat(Infinity), recursion is the clean mental model.

function flatten(arr) {
    return arr.reduce((acc, val) => {
        if (Array.isArray(val)) {
            return acc.concat(flatten(val));
        }
        return acc.concat(val);
    }, []);
}

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

Walking through this with [1, [2, [3]]]:

  1. Element 1 is not an array, push it: [1]

  2. Element [2, [3]] is an array, recurse into it

    • Element 2 is not an array, push it: [2]

    • Element [3] is an array, recurse into it

      • Element 3 is not an array, push it: [3]
  3. Concat everything back up: [1, 2, 3]


Approach 4: Stack-Based Iterative Flattening

Recursion works, but for very deeply nested arrays you can hit the call stack limit. An iterative approach using a stack avoids that:

function flattenIterative(arr) {
    const stack = [...arr];
    const result = [];

    while (stack.length) {
        const item = stack.pop();

        if (Array.isArray(item)) {
            stack.push(...item);
        } else {
            result.push(item);
        }
    }

    return result.reverse();
}

How this works:

  1. Push all elements onto a stack

  2. Pop the top item

  3. If it's an array, spread its contents back onto the stack

  4. If it's a value, add it to the result

  5. Repeat until the stack is empty

  6. Reverse at the end because you were popping (LIFO order)

This handles deeply nested structures without blowing the stack.


Common Interview Scenarios

"Implement flat() from scratch"

This is asking for the recursive approach. Write it using reduce and Array.isArray. Show that you understand the base case (non-array element) and the recursive case (array element).

"Flatten to a specific depth"

Add a depth parameter to your recursive function:

function flatten(arr, depth = 1) {
    return arr.reduce((acc, val) => {
        if (Array.isArray(val) && depth > 0) {
            return acc.concat(flatten(val, depth - 1));
        }
        return acc.concat(val);
    }, []);
}

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

Each recursive call decrements depth. When it hits 0, stop flattening.

"What if the array is deeply nested and performance matters?"

This is where the iterative stack-based approach comes up. Mention that deep recursion risks a stack overflow and that the iterative version handles that.

"What's the time complexity?"

For any correct flatten, it's O(n) where n is the total number of elements across all levels. You visit each element exactly once.


Summary

Approach Use When
flat(Infinity) You need something that just works and env supports it
reduce + concat One level only, simple case
Recursive flatten() Interview, custom depth control, readable code
Iterative (stack) Very deep nesting, performance-sensitive

Conclusion

The built-in flat() covers most real-world cases. Understanding the recursive approach is what most interviews actually test. And if you can explain why the iterative version exists, that's usually the thing that moves you from "knows the API" to "understands what's happening under the hood."