Skip to content
JavaScript js arrays 4 min read

flat & flatMap

Nested arrays show up constantly — paginated API responses, grouped records, tag lists per item — and flattening them used to require clumsy reduce + concat recipes. ES2019 added two purpose-built array methods that make this clean: flat, which collapses nested arrays into a single level (or several), and flatMap, which maps each element and then flattens the result one level deep. Both return new arrays and never mutate the original.

flat(depth)

Array.prototype.flat(depth) creates a new array with all sub-array elements concatenated into it, recursively up to the specified depth. The depth argument defaults to 1, so a single call flattens one level. Empty slots in sparse arrays are removed in the process.

const nested = [1, [2, 3], [4, [5, 6]]];

console.log(nested.flat());      // depth 1
console.log(nested.flat(2));     // depth 2
console.log(nested.flat(Infinity)); // fully flatten any depth

Output:

[ 1, 2, 3, 4, [ 5, 6 ] ]
[ 1, 2, 3, 4, 5, 6 ]
[ 1, 2, 3, 4, 5, 6 ]

Pass Infinity when you don’t know — or don’t care about — how deeply the data is nested and just want a single flat list. Because flat skips empty slots, it doubles as a quick way to drop holes from a sparse array.

const sparse = [1, , 3, , 5];
console.log(sparse.flat()); // holes removed even at depth 1

Output:

[ 1, 3, 5 ]

Note: flat() only removes holes (empty slots), not undefined values. [1, undefined, 3].flat() still contains undefined. Use filter if you need to strip those too.

flatMap

flatMap(callback) runs a mapping function over every element and then flattens the result by exactly one level. It is equivalent to map() followed by flat(1), but does it in a single pass, which is slightly more efficient and reads more clearly.

const sentences = ["hello world", "foo bar"];

const mapped = sentences.map((s) => s.split(" ")); // array of arrays
const flat = sentences.flatMap((s) => s.split(" ")); // flattened

console.log(mapped);
console.log(flat);

Output:

[ [ 'hello', 'world' ], [ 'foo', 'bar' ] ]
[ 'hello', 'world', 'foo', 'bar' ]

The callback receives the same arguments as map: (element, index, array). Importantly, flatMap only flattens one level — returning a deeply nested array from the callback will not be flattened further.

Filtering and expanding in one step

A powerful idiom: because returning an empty array [] from the callback contributes nothing to the output, flatMap can simultaneously transform and filter. Return [value] to keep, [] to drop.

const numbers = [1, 2, 3, 4, 5];

// Keep evens, doubled — filter + map in a single pass
const result = numbers.flatMap((n) => (n % 2 === 0 ? [n * 2] : []));

console.log(result);

Output:

[ 4, 8 ]

You can also emit more than one element per input, which map alone cannot do without producing a nested array:

const ranges = [1, 2, 3];
const expanded = ranges.flatMap((n) => [n, n * 10]);

console.log(expanded);

Output:

[ 1, 10, 2, 20, 3, 30 ]

flat vs flatMap

MethodMapping callbackFlatten depthTypical use
flat(depth)Nodepth (default 1, Infinity for all)Collapse existing nested arrays
flatMap(fn)YesExactly 1 (not configurable)Map then flatten, or expand/filter in one pass

If you need to flatten more than one level after mapping, use map(...).flat(depth) instead — flatMap cannot go deeper than one level.

Real-world example

Suppose an API returns pages of orders and you want a single list of every line item across all pages.

const pages = [
  { orders: [{ items: ["a", "b"] }, { items: ["c"] }] },
  { orders: [{ items: ["d", "e"] }] },
];

const allItems = pages
  .flatMap((page) => page.orders)   // flatten orders across pages
  .flatMap((order) => order.items); // flatten items across orders

console.log(allItems);

Output:

[ 'a', 'b', 'c', 'd', 'e' ]

Chaining flatMap twice unwinds two levels of nesting cleanly, with each call doing one transform-and-flatten step.

Browser and runtime support

Both methods are part of ES2019 and supported in all modern browsers and Node.js 11+. They are safe to use without polyfills in any current environment, including Deno and Bun.

Best Practices

  • Reach for flatMap whenever you find yourself writing .map(...).flat() — it’s clearer and a touch faster.
  • Use flat(Infinity) only when arbitrary depth is genuinely possible; a fixed depth signals intent better.
  • Remember flatMap flattens exactly one level — chain calls or fall back to map().flat(depth) for deeper structures.
  • Exploit the empty-array trick ([] to drop, [x] to keep) to combine filtering and mapping in a single readable pass.
  • Don’t rely on flat() to remove undefined — it only strips holes; add a filter(Boolean) when you need to drop falsy values.
  • Both methods return new arrays, so they compose well in chains without side effects.
Last updated June 1, 2026
Was this helpful?