Episode 1 — Fundamentals / 1.22 — Array Methods
1.22.h — flat() and flatMap()
In one sentence:
flat(depth)flattens nested arrays down to a specified depth, andflatMap()combines amapfollowed byflat(1)in a single, more efficient step.
Navigation: ← 1.22.g — sort() · 1.22.i — Functional Thinking →
1. flat(depth) — flatten nested arrays
flat() creates a new array with all sub-array elements concatenated up to the specified depth.
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.flat();
console.log(flat); // [1, 2, 3, 4, 5, 6]
console.log(nested); // [[1, 2], [3, 4], [5, 6]] ← unchanged
Syntax
const newArray = arr.flat(depth);
| Parameter | Description | Default |
|---|---|---|
depth | How many levels of nesting to flatten | 1 |
| Return | New flattened array | |
| Mutates? | No |
2. Default depth is 1
flat() with no argument only flattens one level:
const deep = [1, [2, [3, [4]]]];
deep.flat(); // [1, 2, [3, [4]]] — one level flattened
deep.flat(1); // [1, 2, [3, [4]]] — same as above
deep.flat(2); // [1, 2, 3, [4]] — two levels
deep.flat(3); // [1, 2, 3, 4] — three levels
3. flat(Infinity) — flatten all levels
When you do not know the nesting depth, use Infinity:
const crazy = [1, [2, [3, [4, [5, [6]]]]]];
const allFlat = crazy.flat(Infinity);
console.log(allFlat); // [1, 2, 3, 4, 5, 6]
This is safe and commonly used. It will flatten any depth.
4. flat removes holes (sparse arrays)
A bonus behavior: flat removes empty slots:
const sparse = [1, , 3, , 5];
console.log(sparse.flat()); // [1, 3, 5] — holes removed
const nested = [1, , [3, , 5]];
console.log(nested.flat()); // [1, 3, 5] — holes at both levels removed
5. flatMap() — map + flat(1) in one step
flatMap first maps each element using a callback, then flattens the result by one level. It is more efficient than calling .map(...).flat() separately because it does both in a single pass.
const sentences = ["Hello world", "How are you"];
// map then flat
const words1 = sentences.map(s => s.split(" ")).flat();
// [["Hello", "world"], ["How", "are", "you"]].flat()
// ["Hello", "world", "How", "are", "you"]
// flatMap — same result, one step
const words2 = sentences.flatMap(s => s.split(" "));
// ["Hello", "world", "How", "are", "you"]
Syntax
const newArray = arr.flatMap(callback(element, index, array));
| Feature | flatMap |
|---|---|
| Maps each element | Yes |
| Flattens result | One level only |
| Returns | New array |
| Mutates? | No |
6. flatMap for filtering and mapping simultaneously
flatMap can add or remove elements by returning arrays of different lengths:
// Return empty array to remove, single-element array to keep, multi-element to expand
const nums = [1, 2, 3, 4, 5, 6];
const result = nums.flatMap(n => {
if (n % 2 === 0) return [n * 10]; // keep and transform evens
return []; // remove odds
});
console.log(result); // [20, 40, 60]
This is equivalent to .filter(...).map(...) but in a single pass:
// Equivalent:
nums.filter(n => n % 2 === 0).map(n => n * 10); // [20, 40, 60]
Expand elements (one-to-many)
const pairs = [1, 2, 3];
const expanded = pairs.flatMap(n => [n, n * n]);
console.log(expanded); // [1, 1, 2, 4, 3, 9]
7. Use cases
Normalize nested categories
const categories = [
{ name: "Electronics", subcategories: ["Phones", "Laptops", "Tablets"] },
{ name: "Clothing", subcategories: ["Shirts", "Pants"] },
{ name: "Books", subcategories: ["Fiction", "Non-fiction", "Comics"] },
];
// Get all subcategories in a flat list
const allSubs = categories.flatMap(cat => cat.subcategories);
console.log(allSubs);
// ["Phones", "Laptops", "Tablets", "Shirts", "Pants", "Fiction", "Non-fiction", "Comics"]
// Or with category prefix
const labeled = categories.flatMap(cat =>
cat.subcategories.map(sub => `${cat.name} > ${sub}`)
);
console.log(labeled);
// ["Electronics > Phones", "Electronics > Laptops", ...]
Merge nested API results
const pages = [
{ page: 1, results: [{ id: 1 }, { id: 2 }] },
{ page: 2, results: [{ id: 3 }, { id: 4 }] },
{ page: 3, results: [{ id: 5 }] },
];
const allResults = pages.flatMap(p => p.results);
console.log(allResults);
// [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }]
Split and flatten strings
const csvRows = ["a,b,c", "d,e,f", "g,h,i"];
const allCells = csvRows.flatMap(row => row.split(","));
console.log(allCells);
// ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Duplicate with variation
const colors = ["red", "blue", "green"];
const withShades = colors.flatMap(color => [
`light-${color}`,
color,
`dark-${color}`,
]);
console.log(withShades);
// ["light-red", "red", "dark-red", "light-blue", "blue", "dark-blue", ...]
8. flat vs manual flattening
Before flat existed (ES2019)
// Using reduce + concat
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
// Using spread in reduce
const flat2 = nested.reduce((acc, arr) => [...acc, ...arr], []);
// Deep flatten with recursion
function deepFlat(arr) {
return arr.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(deepFlat(val)) : acc.concat(val),
[]
);
}
Now — just use flat
nested.flat(); // one level
nested.flat(Infinity); // all levels
9. flatMap only flattens one level
Important limitation: flatMap is equivalent to map + flat(1), not map + flat(Infinity):
const arr = [1, 2, 3];
const result = arr.flatMap(n => [[n, n * 2]]);
console.log(result); // [[1, 2], [2, 4], [3, 6]]
// The inner arrays remain — only one level was flattened
// If you need deeper flattening:
arr.map(n => [[n, n * 2]]).flat(2);
// [1, 2, 2, 4, 3, 6]
10. Processing tree-like data
const fileSystem = [
{
name: "src",
children: [
{ name: "index.js", children: [] },
{
name: "components",
children: [
{ name: "App.js", children: [] },
{ name: "Header.js", children: [] },
],
},
],
},
{ name: "package.json", children: [] },
];
// Get all file/folder names (one level deep)
const topLevel = fileSystem.flatMap(item => [
item.name,
...item.children.map(c => ` ${c.name}`),
]);
console.log(topLevel);
// ["src", " index.js", " components", "package.json"]
// For full recursive traversal, use a recursive function with flat:
function getAllNames(items, depth = 0) {
return items.flatMap(item => [
" ".repeat(depth) + item.name,
...getAllNames(item.children, depth + 1),
]);
}
console.log(getAllNames(fileSystem));
// ["src", " index.js", " components", " App.js", " Header.js", "package.json"]
11. Performance considerations
flat(1)is well-optimized in modern engines.flat(Infinity)on deeply nested structures is fine for normal use but avoid in hot paths with massive data.flatMapis faster than.map().flat()because it avoids creating the intermediate array.
12. Edge cases
// Empty array
[].flat(); // []
[].flatMap(x => [x]); // []
// Already flat
[1, 2, 3].flat(); // [1, 2, 3] — new array, same values
// Mixed nesting
[1, [2, 3], 4, [5]].flat(); // [1, 2, 3, 4, 5]
// Non-array elements remain
[1, "hello", [2, 3]].flat(); // [1, "hello", 2, 3]
Key takeaways
flat(depth)flattens nested arrays to the specified depth — default is1.flat(Infinity)flattens all nesting levels.flatalso removes holes in sparse arrays.flatMap(fn)=map(fn)+flat(1)in a single, more efficient pass.flatMapcan be used for filter+map, one-to-many expansion, or element removal.- Neither
flatnorflatMapmutates the original array.
Explain-It Challenge
Explain without notes:
- What does
[1, [2, [3]]].flat()return? What about.flat(Infinity)? - How is
flatMapdifferent from just calling.map().flat()? - Write a
flatMapcall that takes["hello world", "foo bar"]and produces["hello", "world", "foo", "bar"].
Navigation: ← 1.22.g — sort() · 1.22.i — Functional Thinking →