🪆 Flattening Arrays in JavaScript — The Complete Visual Guide
Imagine a Russian nesting doll. You open one, find another. Open that, find another. That's a nested array. Our job? Unpack them all into one flat row.
📦 What Are Nested Arrays?
A nested array is simply an array that contains other arrays as its elements. Arrays inside arrays. Boxes inside boxes.
// Simple flat array
const flat = [1, 2, 3, 4, 5];
// Nested array — arrays inside arrays
const nested = [1, [2, 3], [4, [5, 6]], [[[7]]]];
Visualizing the layers:
[1, [2, 3], [4, [5, 6]], [[[7]]]]
^ ^^^^^ ^^^^^^^^^^ ^^^^^^
| | | |
| depth 1 depth 1+2 depth 1+2+3
depth 0 (top level)
These show up all the time in real code:
- 🌐 API responses — deeply nested JSON objects
- 🗃️ Database results — grouped or paginated data
- 📁 File system trees — folders containing folders
- 🧮 Matrix math — rows and columns
- 🗺️ Category trees — parent/child hierarchies
🤔 Why Flatten at All?
Here's the problem with nested arrays:
const scores = [[85, 92], [78, 95], [88, 71]];
// ❌ .includes() won't work as expected
scores.includes(85); // false — it's looking for [85, 92]
// ❌ .length is misleading
scores.length; // 3 — but there are 6 actual scores
// ❌ Can't sort all values directly
scores.sort(); // sorts sub-arrays, not values
After flattening:
const flat = scores.flat();
// → [85, 92, 78, 95, 88, 71]
flat.includes(85); // ✅ true
flat.length; // ✅ 6
flat.sort((a, b) => a - b); // ✅ [71, 78, 85, 88, 92, 95]
🧠 The Core Concept
Flattening = pulling all values out of their nested containers into a single flat array.
BEFORE: AFTER:
┌──────────────────────┐ ┌────────────────────────────┐
│ [ │ │ │
│ 1, │ │ [ 1, 2, 3, 4, 5, 6, 7 ] │
│ [2, 3], │ ──► │ │
│ [4, [5, 6]], │ └────────────────────────────┘
│ [[[7]]] │
│ ] │
└──────────────────────┘
The key variable: how deep do you need to flatten?
| Depth | What it does |
|---|---|
1 |
Unwraps only the outermost arrays |
2 |
Unwraps two levels |
Infinity |
Fully flattens everything |
🛠️ Every Method, Explained
1️⃣ Array.flat() — The Native, Modern Way
flat() was introduced in ES2019 and is the most readable, idiomatic solution.
const arr = [1, [2, 3], [4, [5, 6]], [[[7]]]];
arr.flat(); // depth 1 (default)
// → [1, 2, 3, 4, [5, 6], [[[7]]]]
arr.flat(2); // depth 2
// → [1, 2, 3, 4, 5, 6, [[[7]]]]
arr.flat(Infinity); // flatten everything
// → [1, 2, 3, 4, 5, 6, 7]
Step-by-step for flat(1):
Input: [1, [2, 3], [4, [5, 6]]]
1 → not an array → keep: 1
[2, 3] → array at depth 1 → unwrap: 2, 3
[4, [5,6]]→ array at depth 1 → unwrap: 4, [5,6]
↑ still nested (depth 2)
Output: [1, 2, 3, 4, [5, 6]]
✅ Use when: Working in modern codebases. Clean, expressive, handles depth.
2️⃣ flatMap() — Map + Flatten in One Pass
flatMap() maps each element and then flattens one level. It's like a supercharged .map().
const words = ["Hello World", "JS is great"];
words.map(w => w.split(" "));
// → [["Hello", "World"], ["JS", "is", "great"]]
words.flatMap(w => w.split(" "));
// → ["Hello", "World", "JS", "is", "great"]
Real-world: extract all tags from a blog array:
const posts = [
{ title: "Async Await", tags: ["js", "async"] },
{ title: "CSS Grid", tags: ["css", "layout"] },
{ title: "Node.js", tags: ["node", "js"] },
];
const allTags = posts.flatMap(p => p.tags);
// → ["js", "async", "css", "layout", "node", "js"]
const uniqueTags = [...new Set(allTags)];
// → ["js", "async", "css", "layout", "node"]
✅ Use when: You need to transform + flatten simultaneously. Only goes 1 level deep.
3️⃣ reduce() — The Foundational Approach
Before flat() landed in ES2019, reduce() was the standard approach. Still great to know.
const arr = [1, [2, 3], [4, 5]];
const flat = arr.reduce((acc, val) => acc.concat(val), []);
// → [1, 2, 3, 4, 5]
Step-by-step accumulation:
Initial: acc = []
Item 1: val = 1 → [].concat(1) = [1]
Item 2: val = [2, 3] → [1].concat([2,3]) = [1, 2, 3]
Item 3: val = [4, 5] → [1,2,3].concat([4,5]) = [1, 2, 3, 4, 5]
For deep flattening with reduce — use recursion:
function deepFlatReduce(arr) {
return arr.reduce((acc, val) =>
Array.isArray(val)
? acc.concat(deepFlatReduce(val))
: acc.concat(val),
[]);
}
deepFlatReduce([1, [2, [3, [4]]]]);
// → [1, 2, 3, 4]
✅ Use when: You want custom logic while flattening (filter, transform, accumulate).
4️⃣ Recursive Flatten — The Interview Essential
This is the approach you must be able to write from scratch in interviews.
function flatten(arr) {
const result = [];
for (const item of arr) {
if (Array.isArray(item)) {
result.push(...flatten(item)); // recurse deeper
} else {
result.push(item); // base case
}
}
return result;
}
flatten([1, [2, [3, [4, [5]]]]]);
// → [1, 2, 3, 4, 5]
Call stack diagram for flatten([1, [2, [3]]]):
flatten([1, [2, [3]]])
├─ item = 1 → push 1
└─ item = [2, [3]] → recurse:
flatten([2, [3]])
├─ item = 2 → push 2
└─ item = [3] → recurse:
flatten([3])
└─ item = 3 → push 3
return [3]
return [2, 3]
push ...2, 3
return [1, 2, 3]
✅ Use when: Interviews, learning, or environments without ES2019 support.
5️⃣ Iterative Stack — Recursion-Safe 🔐
Deep recursion can cause stack overflow for extremely nested arrays. This stack-based approach avoids that:
function flattenSafe(arr) {
const stack = [...arr];
const result = [];
while (stack.length) {
const item = stack.pop();
if (Array.isArray(item)) {
stack.push(...item); // push elements back to process
} else {
result.unshift(item); // prepend to maintain order
}
}
return result;
}
flattenSafe([1, [2, [3, [4, [5]]]]]);
// → [1, 2, 3, 4, 5]
✅ Use when: Handling user-generated or untrusted deeply nested data.
📊 Quick Comparison
| Method | Depth Control | ES Version | Best For |
|---|---|---|---|
flat() |
✅ Configurable | ES2019 | Most use cases |
flatMap() |
1 level only | ES2019 | Map + flatten |
reduce() |
With recursion | ES5 | Custom logic |
| Recursion | ✅ Full | ES5 | Interviews |
| Stack iterative | ✅ Full | ES5 | Very deep nesting |
🎯 Interview Questions You'll Actually Face
❓ "Write your own implementation of flat()"
Array.prototype.myFlat = function(depth = 1) {
const result = [];
function walk(arr, d) {
for (const item of arr) {
if (Array.isArray(item) && d > 0) {
walk(item, d - 1);
} else {
result.push(item);
}
}
}
walk(this, depth);
return result;
};
[1, [2, [3]]].myFlat(1); // [1, 2, [3]]
[1, [2, [3]]].myFlat(Infinity); // [1, 2, 3]
❓ "Flatten and remove duplicates"
const arr = [[1, 2], [2, 3], [3, [3, 4]]];
[...new Set(arr.flat(Infinity))];
// → [1, 2, 3, 4]
❓ "Flatten only arrays, keep objects intact"
function flattenArraysOnly(arr) {
return arr.reduce((acc, val) => {
if (Array.isArray(val)) {
acc.push(...flattenArraysOnly(val));
} else {
acc.push(val); // objects, strings, numbers — kept as-is
}
return acc;
}, []);
}
flattenArraysOnly([1, [2, { a: 3 }], [[4]]]);
// → [1, 2, { a: 3 }, 4]
❓ "Find the max nesting depth"
function maxDepth(arr) {
return Array.isArray(arr)
? 1 + Math.max(0, ...arr.map(maxDepth))
: 0;
}
maxDepth([1, [2, [3, [4]]]]);
// → 4
❓ "Flatten and extract values from objects in an array"
const data = [
{ name: "Alice", hobbies: ["reading", "coding"] },
{ name: "Bob", hobbies: ["gaming", "cooking"] },
];
data.flatMap(person => person.hobbies);
// → ["reading", "coding", "gaming", "cooking"]
🧾 Cheat Sheet
const arr = [1, [2, [3, [4]]]];
// ── Native flat ──────────────────────────────
arr.flat() // [1, 2, [3, [4]]] (depth 1)
arr.flat(2) // [1, 2, 3, [4]] (depth 2)
arr.flat(Infinity) // [1, 2, 3, 4] (full)
// ── flatMap ──────────────────────────────────
[[1,2],[3,4]].flatMap(x => x) // [1, 2, 3, 4]
// ── reduce ───────────────────────────────────
arr.reduce((a,v) => a.concat(v), [])
// [1, 2, [3, [4]]] (depth 1)
// ── recursive ────────────────────────────────
function df(a) {
return a.reduce((acc, v) =>
acc.concat(Array.isArray(v) ? df(v) : v), []);
}
df(arr) // [1, 2, 3, 4]
🏆 Key Takeaways
- 🥇
flat(Infinity)is the cleanest way to fully flatten in modern JS - ⚡
flatMap()is perfect when you're transforming data at the same time - 🧠 Recursive flatten is a must-know for interviews — understand it cold
- 🔒 Iterative/stack approach is safest for arbitrarily deep or unknown nesting
- 🤔 Always ask yourself: "How many levels deep do I actually need?"
💻 Try It!
// Flatten, deduplicate, and sort
const challenge = [[5, 3], [1, [9, 2]], [[[8, 4]]]];
const result = [...new Set(challenge.flat(Infinity))]
.sort((a, b) => a - b);
console.log(result);
// → [1, 2, 3, 4, 5, 8, 9]
If this helped, drop a ❤️ and share it with a fellow dev!
Top comments (0)