jsguides

Array.prototype.flat()

flat(depth)

The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth. It’s the cleanest way to flatten nested arrays without writing recursive logic yourself.

Syntax

array.flat(depth)
  • depth: The depth level specifying how deep a nested array should be flattened. Defaults to 1. Use Infinity to flatten all nested arrays.
  • Returns: A new flattened array.

The method always produces a fresh array — it never modifies the original. If the array is already flat, calling flat() returns a shallow copy with the same elements in the same order.

TypeScript Signature

interface Array<T> {
  flat<U>(this: U[][], depth: 1): U[];
  flat<U>(this: U[][], depth: 0): U[][];
  flat<U>(this: any[], depth: number): U[];
}

The TypeScript overloads reflect how the return type narrows based on the depth argument. Passing depth: 1 on a two-dimensional array produces a flat U[], while depth: 0 returns the input type unchanged.

Parameters

ParameterTypeDefaultDescription
depthnumber1The nesting depth level to flatten. Infinity flattens all levels.

Examples

Basic Flattening

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

// Default depth of 1
console.log(nested.flat());
// [1, 2, 3, 4, [5, 6]]

// Flatten one level deep
console.log(nested.flat(1));
// [1, 2, 3, 4, [5, 6]]

Flattening to different depths

The depth parameter gives you precise control over how many nesting levels to remove. This matters when you have a known structure — like a three-level tree — and only want to collapse the top layer while keeping deeper groupings intact.

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

// Depth 0 - no flattening
console.log(deep.flat(0));
// [1, [2, [3, [4, [5]]]]]

// Depth 1
console.log(deep.flat(1));
// [1, 2, [3, [4, [5]]]]

// Depth 2
console.log(deep.flat(2));
// [1, 2, 3, [4, [5]]]

// Infinity - fully flatten
console.log(deep.flat(Infinity));
// [1, 2, 3, 4, 5]

Removing empty slots

Sparse arrays — arrays with holes left by delete or trailing commas — are common in real-world data. Calling flat() on them eliminates those gaps along with any nesting, so you get a dense result without extra cleanup steps.

const sparse = [1, 2, , 4, 5];

// flat() automatically removes empty slots
console.log(sparse.flat());
// [1, 2, 4, 5]

Combining with other array methods

flat() works well at the end of a chain of map() or filter() calls. When each step produces a nested structure, flattening at the end collects everything into one list without the need for a custom reducer. The flatMap() method seen below combines both steps into a single call, which can be more concise when the transform-and-flatten pattern is the only thing you need.

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

// Flatten after mapping
const result = data.map(pair => pair.map(x => x * 2)).flat();
console.log(result);
// [2, 4, 6, 8, 10, 12]

// This is equivalent to flatMap()
console.log(data.flatMap(pair => pair.map(x => x * 2)));
// [2, 4, 6, 8, 10, 12]

Practical example: processing API responses

A typical API payload returns nested user objects with arrays of associated data. Extracting and flattening those sub-arrays is a one-liner with flat(), and the resulting flat list feeds directly into aggregation methods like reduce(). This is a common pattern for computing totals, averages, or percentiles from nested response data.

const apiResponse = {
  users: [
    { name: "Alice", scores: [95, 87] },
    { name: "Bob", scores: [92, 88, 91] },
    { name: "Carol", scores: [78] }
  ]
};

// Get all scores in a flat array
const allScores = apiResponse.users.map(u => u.scores).flat();
console.log(allScores);
// [95, 87, 92, 88, 91, 78]

// Calculate average
const avg = allScores.reduce((a, b) => a + b, 0) / allScores.length;
console.log(avg.toFixed(2));
// "88.50"

Common Patterns

Flatten object values

Object.values() returns an array of arrays when your object properties hold arrays. Chaining .flat() afterwards collapses those sub-arrays into a single list, which is a clean pattern for collecting nested data from configuration objects or grouped results. No intermediate variable or manual loop is needed.

const groups = {
  a: [1, 2],
  b: [3, 4],
  c: [5, 6]
};

const values = Object.values(groups).flat();
console.log(values);
// [1, 2, 3, 4, 5, 6]

Flatten while filtering

Flattening first and then filtering is a useful pattern when you need all values from a nested structure but only want to keep those that match a condition. The two operations compose naturally because both return new arrays without side effects. You can chain flat(), filter(), and then sort() or reduce() for multi-step data cleanup in a single expression.

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

// Flatten and filter in one chain
const nonZero = matrix.flat().filter(x => x !== 0);
console.log(nonZero);
// [1, 2, 3, 4, 5, 6]

Edge Cases

A few scenarios produce results that are worth knowing before they trip you up. Empty sub-arrays collapse to nothing, sparse holes vanish, and non-array items pass through without modification. Each behavior is consistent once you remember that flat() only looks one level deeper at a time.

// Empty arrays are preserved
console.log([1, [], 2].flat());
// [1, 2]

// Holes are removed
console.log([1, 2, , 3, , 4].flat());
// [1, 2, 3, 4]

// Non-array elements are passed through
console.log([1, "two", 3].flat());
// [1, "two", 3]

Performance Note

For very deep arrays, flat(Infinity) can be slow. Consider using a custom recursive function if you need more control or better performance with massive arrays.

How flat() handles non-array elements

flat() only flattens actual Array instances. Strings, numbers, objects, and other non-array values are passed through as-is, even if they are array-like. This means [{ 0: 'a', length: 1 }].flat() returns the object unchanged - only true arrays are flattened.

flat() vs concat() spread

Before flat() was available, the common workaround for depth-1 flattening was [].concat(...nestedArray). The spread syntax has a call stack limit for very large arrays; flat() does not have this limitation and is clearer in intent.

const arr = [[1, 2], [3, 4]];

// Old approach - can fail for very large arrays
[].concat(...arr); // [1, 2, 3, 4]

// Modern - flat() and no stack limit concern
arr.flat(); // [1, 2, 3, 4]

flat() and sparse arrays

flat() removes holes from sparse arrays at the levels it flattens. A hole at depth 1 in a flat(1) call will not appear in the result. Holes that survive flattening (because they are deeper than the specified depth) are preserved as undefined in the output. This differs from map(), which preserves holes without visiting them.

Performance for deeply nested structures

For moderately sized arrays, flat(Infinity) is convenient but can be slow on deeply recursive structures because it processes the entire tree before returning. If you know the exact nesting depth, specifying it explicitly (e.g., flat(2)) is faster than flat(Infinity) because the engine can short-circuit once the target depth is reached. For very large or deeply nested data, consider a manual recursive reducer if performance is critical.

When flat() is the better fit

flat() is the right choice when nested arrays are an implementation detail and the rest of your code wants a single, linear list. That happens often after grouping, mapping, or collecting values from an API response. Instead of writing your own recursive helper, you can flatten the structure with one method call and keep the transformation easy to read.

It also pairs well with filtering and aggregation. Once the values are in a single array, methods like filter(), reduce(), and sort() become simpler to apply because you no longer need to account for nested levels in each step.

See Also