JS Array Flat Method: Flatten Nested Arrays Fast
Master the JavaScript flat() method for flattening nested arrays. Covers syntax, depth control, removing holes, comparison with flatMap and reduce, real-world patterns, and common mistakes.
The flat() method creates a new array with all sub-array elements pulled up to a specified depth. A single call to flat() turns [[1, 2], [3, 4]] into [1, 2, 3, 4]. It also removes empty holes from sparse arrays. Introduced in ES2019, flat() replaces the verbose reduce() + concat() flattening patterns that developers used for years.
What flat() Does
flat() takes nested arrays and collapses them by the specified depth level:
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.flat();
console.log(flat); // [1, 2, 3, 4, 5, 6]
// Original unchanged
console.log(nested); // [[1, 2], [3, 4], [5, 6]]Syntax
const newArray = array.flat(depth)| Parameter | Description | Default |
|---|---|---|
depth | How many levels of nesting to flatten | 1 |
Return Value
A new array with sub-arrays flattened to the specified depth. The original array is never modified.
Depth Control
Default Depth (1)
const data = [1, [2, 3], [4, [5, 6]]];
const flat1 = data.flat(); // depth = 1 (default)
console.log(flat1); // [1, 2, 3, 4, [5, 6]]
// One level flattened: [2, 3] spread out, but [5, 6] inside [4, ...] stays nestedDepth 2
const data = [1, [2, [3, [4, [5]]]]];
const flat2 = data.flat(2);
console.log(flat2); // [1, 2, 3, [4, [5]]]Infinity (Flatten Everything)
const deeplyNested = [1, [2, [3, [4, [5, [6, [7]]]]]]];
const fullyFlat = deeplyNested.flat(Infinity);
console.log(fullyFlat); // [1, 2, 3, 4, 5, 6, 7]| Depth | Input | Result |
|---|---|---|
1 | [1, [2, [3]]] | [1, 2, [3]] |
2 | [1, [2, [3]]] | [1, 2, 3] |
Infinity | [1, [2, [3, [4]]]] | [1, 2, 3, 4] |
Use Infinity Carefully
Passing Infinity flattens every level of nesting regardless of depth. This is convenient but can hide bugs if deeply nested structures exist where nesting carries meaning (like tree data). Use a specific depth when you know the expected structure.
Removing Sparse Array Holes
flat() also removes empty slots from sparse arrays:
const sparse = [1, , 3, , 5];
console.log(sparse.flat()); // [1, 3, 5]
const nestedSparse = [1, , [2, , 3], [, 4]];
console.log(nestedSparse.flat()); // [1, 2, 3, 4]This is a convenient side effect: flat() removes sparse array holes during flattening:
const holey = [1, , 2, , 3];
const clean = holey.flat(); // Removes holes and returns [1, 2, 3]
console.log(clean); // [1, 2, 3]Real-World Patterns
Merging API Responses
When multiple API calls return arrays and you need a single combined list:
const page1 = [{ id: 1, name: "Widget" }, { id: 2, name: "Gadget" }];
const page2 = [{ id: 3, name: "Gizmo" }, { id: 4, name: "Doohickey" }];
const page3 = [{ id: 5, name: "Thingamajig" }];
const allPages = [page1, page2, page3];
const allProducts = allPages.flat();
console.log(allProducts.length); // 5Extracting Nested Tags
const articles = [
{ title: "React Basics", tags: ["React", "Frontend"] },
{ title: "Node.js Guide", tags: ["Node.js", "Backend"] },
{ title: "Full Stack", tags: ["React", "Node.js", "Full Stack"] },
];
const allTags = articles.map(a => a.tags).flat();
console.log(allTags);
// ["React", "Frontend", "Node.js", "Backend", "React", "Node.js", "Full Stack"]
// Remove duplicates
const uniqueTags = [...new Set(allTags)];
console.log(uniqueTags);
// ["React", "Frontend", "Node.js", "Backend", "Full Stack"]This pattern (map then flat) is so common that JavaScript provides flatMap() as a single-method shortcut.
Processing Nested File Structures
const fileSystem = [
{ folder: "src", files: ["index.ts", "app.ts"] },
{ folder: "lib", files: ["utils.ts", "helpers.ts", "constants.ts"] },
{ folder: "tests", files: ["app.test.ts"] },
];
const allFiles = fileSystem.map(dir =>
dir.files.map(f => `${dir.folder}/${f}`)
).flat();
console.log(allFiles);
// ["src/index.ts", "src/app.ts", "lib/utils.ts", "lib/helpers.ts", "lib/constants.ts", "tests/app.test.ts"]Normalizing Inconsistent API Data
// Some entries are arrays, some are single values
const rawInput = ["alice", ["bob", "carol"], "dave", ["eve", "frank", "grace"]];
const normalized = rawInput.flat();
console.log(normalized);
// ["alice", "bob", "carol", "dave", "eve", "frank", "grace"]flat() vs reduce() + concat()
Before flat() existed, flattening required manual iteration:
const nested = [[1, 2], [3, 4], [5, 6]];
// Old way: reduce + concat
const flatOld = nested.reduce((acc, arr) => acc.concat(arr), []);
// New way: flat()
const flatNew = nested.flat();
// Both produce: [1, 2, 3, 4, 5, 6]For deep flattening, the old recursive approach was even more verbose:
// Old way: recursive function
function deepFlatten(arr) {
return arr.reduce((acc, val) => {
return acc.concat(Array.isArray(val) ? deepFlatten(val) : val);
}, []);
}
// New way
const result = deeplyNested.flat(Infinity);| Approach | Readability | Lines of Code | Deep Flattening |
|---|---|---|---|
flat() | Excellent | 1 | flat(Infinity) |
flat(depth) | Excellent | 1 | flat(2), flat(3), etc. |
| reduce() + concat | Fair | 1 (single level only) | Requires recursion |
| Recursive function | Poor | 5+ | Manual implementation |
flat() vs flatMap()
flat() only flattens. flatMap() maps then flattens one level:
const sentences = ["Hello world", "Good morning", "Nice day"];
// map + flat (two passes)
const words1 = sentences.map(s => s.split(" ")).flat();
// flatMap (one pass)
const words2 = sentences.flatMap(s => s.split(" "));
// Both: ["Hello", "world", "Good", "morning", "Nice", "day"]| Method | Maps | Flattens | Max Depth |
|---|---|---|---|
flat() | No | Yes | Any (configurable) |
flatMap() | Yes | Yes | 1 (always) |
map().flat() | Yes | Yes | Any (configurable) |
Common Mistakes
Expecting flat() to mutate the original:
const data = [[1, 2], [3, 4]];
// flat() returns a NEW array
data.flat();
console.log(data); // [[1, 2], [3, 4]] — unchanged!
// Fix: assign the result
const result = data.flat();
console.log(result); // [1, 2, 3, 4]Using flat() when flatMap() is more appropriate:
const users = [
{ name: "Alice", hobbies: ["reading", "hiking"] },
{ name: "Bob", hobbies: ["gaming", "cooking"] },
];
// Two-pass approach (wasteful intermediate array)
const hobbies1 = users.map(u => u.hobbies).flat();
// Better: single-pass with flatMap()
const hobbies2 = users.flatMap(u => u.hobbies);Using the wrong method to remove sparse holes:
const sparse = [1, , 3, , 5];
// flat() removes holes reliably
const clean = sparse.flat();
console.log(clean); // [1, 3, 5]
// For arrays without nesting, filter() also works
const cleaned = sparse.filter(() => true);
console.log(cleaned); // [1, 3, 5]Flattening too deeply:
// Data where nesting carries meaning
const orgChart = [
"CEO",
["VP Engineering", ["Senior Dev", "Junior Dev"]],
["VP Marketing", ["Designer"]],
];
// flat(Infinity) destroys the hierarchy
const destroyed = orgChart.flat(Infinity);
console.log(destroyed);
// ["CEO", "VP Engineering", "Senior Dev", "Junior Dev", "VP Marketing", "Designer"]
// The organizational structure is lostBest Practices
- Use flat() for one-level nesting by default. Most real-world data has one level of nested arrays (API responses, grouped data).
- Specify an explicit depth when you know the structure.
flat(2)is clearer thanflat(Infinity)when the data is always two levels deep. - Prefer flatMap() for map-then-flatten patterns. It is more efficient (single pass) and more readable.
- Use flat() to clean sparse arrays. Calling
flat()on a non-nested sparse array removes empty holes. - Do not flatten meaningful hierarchies. If nesting represents a tree, parent-child relationship, or organizational structure, flattening destroys information.
Rune AI
Key Insights
- Non-mutating: flat() returns a new array; the original stays unchanged
- Default depth is 1: flattens one level of nesting; pass a number or Infinity for deeper
- Removes sparse holes: flat() cleans empty slots from arrays as a side effect
- flatMap() for map + flatten: prefer flatMap() when you need to transform and flatten in one step
- Do not over-flatten: use a specific depth when nesting structure carries meaning
Frequently Asked Questions
Does flat() modify the original array?
What does flat(Infinity) do?
Can flat() remove undefined values?
What is the difference between flat() and flatMap()?
Is flat() supported in all browsers?
Conclusion
The flat() method replaces verbose flattening patterns with a single, readable call. For one-level nesting (the most common case), call flat() with no arguments. For deeper structures, pass a specific depth or use Infinity for complete flattening. When the operation is "transform each element and then flatten," use flatMap() instead. The key rules are to always assign the result (since flat() does not mutate), to use explicit depth values when the structure is known, and to avoid flattening hierarchies where nesting carries meaning.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.