
Flattening arrays means collapsing nested structures into a single level. This post covers what nested arrays are, why you'd want to flatten them, and every real approach you'll encounter, including the ones that show up in interviews.
Picture a set of Russian nesting dolls. Each doll contains another, and you have to keep opening them to find the ones inside. Now imagine someone asked you to line all the dolls up in a single row. That's exactly what flattening an array does: it reaches into every layer and pulls everything out into one flat list.
A nested array is an array that contains other arrays as elements. JavaScript doesn't restrict how deep this nesting can go.
javascriptconst flat = [1, 2, 3]; // 1 level const nested = [1, [2, 3], 4]; // 2 levels const deepNest = [1, [2, [3, [4, [5]]]]]; // 5 levels
Visually, here's what that structure looks like:
deepNest └── 1 └── [ 2 └── [ 3 └── [ 4 └── [ 5 ] ] ] ]
Each bracket is a layer you have to unwrap to reach the values inside.
Real-world data comes in all shapes. An API returns paginated results as arrays of arrays. A recursive function builds up nested collections. A multi-dimensional matrix needs to be processed as a stream. In all these cases, you need a single, ordered list before you can sort, filter, map, or reduce it.
javascript// Result from fetching multiple pages const pages = [[{ id: 1 }, { id: 2 }], [{ id: 3 }, { id: 4 }], [{ id: 5 }]]; // You want this // [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]
Before you can do anything useful with pages, you have to flatten it.
Input: [ 1, [ 2, 3 ], [ 4, [ 5, 6 ] ] ] ↓ Step 1: [ 1, 2, 3, 4, [ 5, 6 ] ] ← depth 1 ↓ Step 2: [ 1, 2, 3, 4, 5, 6 ] ← depth 2 (fully flat)
Each pass through removes one layer of nesting. How many passes you need depends on how deep the array goes.
Array.prototype.flat() - The Modern StandardIntroduced in ES2019, flat() is the cleanest solution. It takes an optional depth argument.
javascriptconst arr = [1, [2, [3, [4]]]]; arr.flat(); // [1, 2, [3, [4]]] - default depth is 1 arr.flat(2); // [1, 2, 3, [4]] arr.flat(3); // [1, 2, 3, 4] arr.flat(Infinity); // [1, 2, 3, 4] - flattens everything
Infinity is the go-to when you don't know or don't care how deep the nesting goes. It's readable, concise, and expressive.
reduce() + concat() - One Level, ManuallyUnderstanding this approach is where the real learning happens. It's the building block for everything else.
javascriptconst arr = [1, [2, 3], [4, 5]]; const result = arr.reduce((acc, val) => acc.concat(val), []); // [1, 2, 3, 4, 5]
Initial acc: [] Pass 1: acc = [].concat(1) → [1] Pass 2: acc = [1].concat([2, 3]) → [1, 2, 3] Pass 3: acc = [1, 2, 3].concat([4, 5]) → [1, 2, 3, 4, 5]
concat unwraps one level of nesting automatically. If an element is an array, its contents get merged in. If it's a primitive, it just gets appended.
When you need arbitrary depth without reaching for flat(Infinity), recursion is the answer. It's also the approach interviewers love to ask about.
javascriptfunction flattenDeep(arr) { return arr.reduce((acc, val) => { return Array.isArray(val) ? acc.concat(flattenDeep(val)) // go deeper : acc.concat(val); // it's a value, keep it }, []); } flattenDeep([1, [2, [3, [4]]]]); // [1, 2, 3, 4]
The logic: for each element, check if it's an array. If yes, recurse. If no, push it into the accumulator. Simple mental model, powerful result.
flattenDeep([1, [2, [3]]]) ↓ 1 → not array, add to acc ↓ [2, [3]] → is array, recurse ↓ 2 → not array, add ↓ [3] → is array, recurse ↓ 3 → not array, add Result: [1, 2, 3]
"Flatten without using flat()" - They want to see reduce + recursion. Write flattenDeep from scratch.
"Flatten to a specific depth" - Add a depth parameter and decrement it on each recursive call:
javascriptfunction flattenToDepth(arr, depth = 1) { return arr.reduce((acc, val) => { if (Array.isArray(val) && depth > 0) { return acc.concat(flattenToDepth(val, depth - 1)); } return acc.concat(val); }, []); } flattenToDepth([1, [2, [3, [4]]]], 2); // [1, 2, 3, [4]]
"What's the time complexity?" - O(n) where n is the total number of elements across all levels. You visit each element exactly once.
"What if the array contains non-array objects?" - Array.isArray() handles this correctly. Objects, strings, numbers, null: anything that isn't an array gets added as-is.
javascriptflattenDeep([1, { a: 2 }, [3, null, [true]]]); // [1, { a: 2 }, 3, null, true]
flat(Infinity) is your everyday tool. Readable and concise.reduce + concat is the one-level manual version. Know how it works.flattenDeep is what interviewers want to see. The pattern is: check if array, recurse or push.Flattening is a small concept with a big surface area in interviews. The real skill isn't memorizing the APIs. It's understanding that flattening is a traversal problem, and once you see it that way, every variation becomes a minor twist on the same idea.
Related posts based on tags, category, and projects
A practical guide to six essential JavaScript array methods that every developer uses daily, explained from scratch with real examples, comparisons to traditional loops, and a hands-on assignment to cement your understanding.
Destructuring is a concise syntax for pulling values out of arrays and objects into individual variables. This post covers array destructuring, object destructuring, default values, and the real-world patterns where this shines.
Spread and rest both use `...` but do opposite things. Spread expands an iterable into individual elements. Rest collects individual elements into a single array. This post breaks down each one clearly with real examples.
Arrays are one of the first data structures you'll use in JavaScript, and they're more powerful than they look. This post covers everything from creating your first array to looping through it, with the kind of depth that makes it click.