JavaScript Array flat() Method: Flatten Nested Arrays
Master the JavaScript Array flat() method to flatten nested arrays. Learn syntax, depth control, practical examples, and best practices for array flattening.
The flat()
method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth. Introduced in ES2019, it provides a clean and efficient way to flatten nested arrays.
Understanding Array flat()
The flat()
method is designed to handle nested arrays by "flattening" them - combining elements from nested arrays into a single-level array. It's particularly useful when dealing with data structures that contain arrays within arrays.
Syntax
array.flat([depth]);
Parameters
- depth (optional): The depth level specifying how deep a nested array structure should be flattened
- Default value is 1
- Use
Infinity
to flatten all levels - Must be a positive number or Infinity
Return Value
A new array with the sub-array elements concatenated into it.
Basic Usage
Single-Level Flattening
// Default behavior (depth = 1)
const arr1 = [1, 2, [3, 4]];
console.log(arr1.flat()); // [1, 2, 3, 4]
const arr2 = ['a', ['b', 'c'], 'd'];
console.log(arr2.flat()); // ['a', 'b', 'c', 'd']
// Mixed types
const mixed = [1, [true, 'hello'], null, [undefined, []]];
console.log(mixed.flat()); // [1, true, 'hello', null, undefined, []]
Multi-Level Flattening
const nested = [1, [2, [3, [4, [5]]]]];
// Different depth levels
console.log(nested.flat()); // [1, 2, [3, [4, [5]]]]
console.log(nested.flat(2)); // [1, 2, 3, [4, [5]]]
console.log(nested.flat(3)); // [1, 2, 3, 4, [5]]
console.log(nested.flat(4)); // [1, 2, 3, 4, 5]
// Using Infinity for complete flattening
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5]
Practical Examples
Data Processing
// Processing nested data structures
const salesData = [
['Q1', [100, 200, 150]],
['Q2', [300, 250, 400]],
['Q3', [200, 300]],
['Q4', [500, 450, 600, 700]],
];
// Extract all sales figures
const allSales = salesData.map((quarter) => quarter[1]).flat();
console.log(allSales);
// [100, 200, 150, 300, 250, 400, 200, 300, 500, 450, 600, 700]
// Calculate total
const totalSales = allSales.reduce((sum, sale) => sum + sale, 0);
console.log(`Total sales: $${totalSales}`); // Total sales: $3850
Menu Structure Flattening
const menuStructure = [
{
name: 'File',
items: [
'New',
'Open',
{
name: 'Recent',
items: ['Document1', 'Document2', 'Document3'],
},
'Save',
'Exit',
],
},
{
name: 'Edit',
items: ['Cut', 'Copy', 'Paste'],
},
{
name: 'View',
items: [
'Zoom',
{
name: 'Panels',
items: ['Navigator', 'History', 'Layers'],
},
],
},
];
// Extract all menu items
function extractMenuItems(menu) {
return menu
.map((section) => {
return section.items.map((item) =>
typeof item === 'string' ? item : [item.name, ...item.items]
);
})
.flat(2);
}
console.log(extractMenuItems(menuStructure));
// ['New', 'Open', 'Recent', 'Document1', 'Document2', 'Document3', 'Save', 'Exit', 'Cut', 'Copy', 'Paste', 'Zoom', 'Panels', 'Navigator', 'History', 'Layers']
Removing Empty Slots
// flat() removes empty slots in arrays
const sparseArray = [1, , 3, , , 6];
console.log(sparseArray); // [1, empty, 3, empty × 2, 6]
console.log(sparseArray.flat()); // [1, 3, 6]
// Nested sparse arrays
const nestedSparse = [1, [2, , 4], , [5, , , 8], 9];
console.log(nestedSparse.flat()); // [1, 2, 4, 5, 8, 9]
Working with Complex Data
Tree Structure Flattening
class TreeFlattener {
constructor() {
this.nodes = [];
}
// Convert tree to nested array structure
treeToNestedArray(node) {
if (!node) return [];
const result = [node.value];
if (node.children && node.children.length > 0) {
result.push(node.children.map((child) => this.treeToNestedArray(child)));
}
return result;
}
// Flatten tree to get all values
flattenTree(root, depth = Infinity) {
const nestedArray = this.treeToNestedArray(root);
return nestedArray.flat(depth);
}
}
// Example tree
const tree = {
value: 1,
children: [
{
value: 2,
children: [
{ value: 4, children: [] },
{ value: 5, children: [] },
],
},
{
value: 3,
children: [
{
value: 6,
children: [{ value: 7, children: [] }],
},
],
},
],
};
const flattener = new TreeFlattener();
console.log(flattener.flattenTree(tree));
// [1, 2, 4, 5, 3, 6, 7]
Matrix Operations
// 2D Matrix flattening
class Matrix {
constructor(data) {
this.data = data;
}
// Flatten matrix to 1D array
flatten() {
return this.data.flat();
}
// Get all values in row-major order
toArray() {
return this.flatten();
}
// Get specific row
getRow(index) {
return this.data[index];
}
// Get specific column
getColumn(index) {
return this.data.map((row) => row[index]);
}
// Get all unique values
uniqueValues() {
return [...new Set(this.flatten())];
}
}
const matrix = new Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]);
console.log(matrix.flatten()); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(matrix.uniqueValues()); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Combining with Other Methods
flat() with map()
// Traditional approach with map and flat
const sentences = ['Hello world', 'How are you', 'JavaScript is awesome'];
// Split into words - creates nested arrays
const nestedWords = sentences.map((sentence) => sentence.split(' '));
console.log(nestedWords);
// [['Hello', 'world'], ['How', 'are', 'you'], ['JavaScript', 'is', 'awesome']]
// Flatten to get all words
const allWords = nestedWords.flat();
console.log(allWords);
// ['Hello', 'world', 'How', 'are', 'you', 'JavaScript', 'is', 'awesome']
// Or combine in one step (though flatMap is more efficient)
const words = sentences.map((s) => s.split(' ')).flat();
flat() with filter()
// Remove falsy values from nested arrays
const data = [
[1, 0, 2],
[false, 3, null],
[undefined, 4, ''],
[5, NaN, 6],
];
const truthyValues = data.flat().filter(Boolean);
console.log(truthyValues); // [1, 2, 3, 4, 5, 6]
// Extract and filter specific nested data
const users = [
{ name: 'Alice', skills: ['JS', 'React', 'Node'] },
{ name: 'Bob', skills: ['Python', 'JS'] },
{ name: 'Charlie', skills: ['Java', 'Spring', 'JS'] },
];
const allJSUsers = users
.map((user) => (user.skills.includes('JS') ? user.name : []))
.flat();
console.log(allJSUsers); // ['Alice', 'Bob', 'Charlie']
Performance Considerations
Depth Impact
// Performance comparison for different depths
const createDeeplyNested = (depth, size = 1000) => {
let arr = Array(size).fill(1);
for (let i = 0; i < depth; i++) {
arr = [arr];
}
return arr;
};
// Shallow nesting - fast
console.time('Shallow flat');
const shallow = createDeeplyNested(2, 10000);
shallow.flat(2);
console.timeEnd('Shallow flat');
// Deep nesting - slower
console.time('Deep flat');
const deep = createDeeplyNested(10, 1000);
deep.flat(10);
console.timeEnd('Deep flat');
// Infinity - use with caution
console.time('Infinity flat');
const veryDeep = createDeeplyNested(5, 1000);
veryDeep.flat(Infinity);
console.timeEnd('Infinity flat');
Alternative Approaches
// Manual recursive flattening (pre-ES2019)
function flattenRecursive(arr, depth = 1) {
if (depth < 1) return arr.slice();
return arr.reduce((acc, val) => {
if (Array.isArray(val) && depth > 0) {
return acc.concat(flattenRecursive(val, depth - 1));
}
return acc.concat(val);
}, []);
}
// Using spread operator (shallow only)
function flattenShallow(arr) {
return [].concat(...arr);
}
// Performance test
const testArray = [
[1, 2],
[3, 4],
[5, 6],
];
console.time('flat()');
testArray.flat();
console.timeEnd('flat()');
console.time('spread');
flattenShallow(testArray);
console.timeEnd('spread');
console.time('recursive');
flattenRecursive(testArray);
console.timeEnd('recursive');
Edge Cases and Gotchas
Non-Array Elements
// flat() only flattens arrays, not other iterables
const mixed = [
[1, 2],
new Set([3, 4]), // Set is not flattened
'56', // String is not split
{ 0: 7, 1: 8, length: 2 }, // Array-like object is not flattened
[9, 10],
];
console.log(mixed.flat());
// [1, 2, Set(2), '56', {0: 7, 1: 8, length: 2}, 9, 10]
Circular References
// Be careful with circular references
const arr = [1, 2, 3];
arr.push([4, 5, arr]); // Circular reference
try {
// This will work for depth 1
console.log(arr.flat()); // [1, 2, 3, 4, 5, [1, 2, 3, [...]]]
// But Infinity depth may cause issues
// console.log(arr.flat(Infinity)); // May cause stack overflow
} catch (e) {
console.error('Error:', e.message);
}
Preserving Array Properties
// flat() doesn't preserve array properties
const arr = [1, [2, 3], 4];
arr.customProp = 'test';
arr[1].nestedProp = 'nested';
const flattened = arr.flat();
console.log(flattened); // [1, 2, 3, 4]
console.log(flattened.customProp); // undefined
Practical Utilities
Deep Flatten with Type Checking
function safeFlatten(arr, depth = 1) {
if (!Array.isArray(arr)) {
throw new TypeError('First argument must be an array');
}
if (depth !== Infinity && (!Number.isInteger(depth) || depth < 0)) {
throw new RangeError('Depth must be a non-negative integer or Infinity');
}
try {
return arr.flat(depth);
} catch (e) {
console.error('Flattening failed:', e);
return arr;
}
}
// Usage
console.log(
safeFlatten(
[
[1, 2],
[3, 4],
],
1
)
); // [1, 2, 3, 4]
console.log(safeFlatten([[1, [2, [3]]]], Infinity)); // [1, 2, 3]
Flatten with Transformation
function flatMap(arr, callback, depth = 1) {
return arr.map(callback).flat(depth);
}
// Example: Extract and flatten nested properties
const data = [
{ values: [1, 2, 3] },
{ values: [4, 5] },
{ values: [6, 7, 8, 9] },
];
const result = flatMap(data, (item) => item.values);
console.log(result); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Browser Compatibility
The flat()
method is supported in modern browsers but may need a polyfill for older environments:
// Simple polyfill for Array.prototype.flat
if (!Array.prototype.flat) {
Array.prototype.flat = function (depth = 1) {
return depth > 0
? this.reduce(
(acc, val) =>
acc.concat(Array.isArray(val) ? val.flat(depth - 1) : val),
[]
)
: this.slice();
};
}
Best Practices
- Use Appropriate Depth: Don't use
Infinity
unless necessary - Consider flatMap(): For map + flat(1) operations
- Handle Edge Cases: Check for non-array elements and circular references
- Performance: Be mindful of deeply nested structures
- Type Safety: Validate inputs when building utilities
Conclusion
The flat()
method is a powerful tool for working with nested arrays, providing a clean and efficient way to flatten array structures to any desired depth. Whether you're processing data, working with tree structures, or simply need to combine nested arrays, flat()
offers a straightforward solution. Understanding its behavior with different depths and edge cases will help you use it effectively in your JavaScript applications.