Array Flatten in JavaScript: Every Approach You Need to Know

Search for a command to run...

No comments yet. Be the first to comment.
Introduction If you've ever built something with vanilla JavaScript and had to update the UI frequently, you already know the pain. You call document.getElementById, change some text, update a class,
Introduction If you've ever installed and tried out Linux after seeing a cool hacking sequence in a sci-fi movie, you know that opening it feels like stepping into a strange city with many roads named

Introduction Most JavaScript confusion around this comes from one wrong assumption: that this depends on where a function is defined. It doesn't. It depends on who called the function. That one shift

Introduction JavaScript gives you objects and arrays out of the box, and for a long time those two were the default answer to almost every data storage problem. Objects for key-value pairs, arrays for

Introduction Most JavaScript codebases are full of code like this: const user = { name: "Sara", age: 28, city: "Mumbai" }; const name = user.name; const age = user.age; const city = user.city; Three

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.
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]
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]]].
flat() MethodJavaScript 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():
It iterates each element
If the element is an array, it spreads its contents one level up
Non-array elements stay as-is
Simple. Use this when you can.
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].
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]]]:
Element 1 is not an array, push it: [1]
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
3 is not an array, push it: [3]Concat everything back up: [1, 2, 3]
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:
Push all elements onto a stack
Pop the top item
If it's an array, spread its contents back onto the stack
If it's a value, add it to the result
Repeat until the stack is empty
Reverse at the end because you were popping (LIFO order)
This handles deeply nested structures without blowing the stack.
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).
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.
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.
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.
| 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 |
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."