Array Flatten in JavaScript: Every Approach You Need to Know

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():
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.
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,
concatit onto the accumulatorconcatautomatically 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]]]:
Element
1is not an array, push it:[1]Element
[2, [3]]is an array, recurse into itElement
2is not an array, push it:[2]Element
[3]is an array, recurse into it- Element
3is not an array, push it:[3]
- Element
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:
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.
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."




