How to Flatten an Array in JavaScript (Multiple Methods Explained)
Introduction
Have you ever worked with data that's organized in multiple layers, like a nested folder structure or a hierarchy of categories? In JavaScript, this is represented as nested arrays - arrays within arrays. While nested arrays are useful for representing hierarchical data, sometimes you need to flatten them into a single-level array. Array flattening is a common task in real-world applications, from processing API responses to organizing data from databases. In this blog, we'll explore what nested arrays are, why we need to flatten them, and learn multiple techniques to flatten arrays efficiently.
1. What Are Nested Arrays?
Nested arrays are arrays that contain other arrays as elements. Think of them like Russian dolls, where one doll contains another, which contains another.
Simple Example:
// Not nested - single level
const simple = [1, 2, 3, 4, 5];
console.log(simple); // [1, 2, 3, 4, 5]
// Nested - contains arrays inside
const nested = [1, [2, 3], 4, [5, 6]];
console.log(nested); // [1, [2, 3], 4, [5, 6]]
// Deeply nested - arrays within arrays within arrays
const deeplyNested = [1, [2, [3, [4, [5]]]]];
console.log(deeplyNested); // [1, [2, [3, [4, [5]]]]]
Real-World Examples:
// Example 1: User groups and members
const groups = [
["Raj", "Priya"],
["Anil", "Sneha", "Vikram"],
["Arjun"]
];
// Example 2: Product categories
const categories = [
["Electronics", ["Phones", "Laptops"]],
["Clothing", ["Men", "Women"]]
];
// Example 3: Geographic locations
const locations = [
["Jaipur", ["Jawahar Nagar", "C-Scheme"]],
["Delhi", ["Dwarka", ["Sector 1", "Sector 2"]]],
["Mumbai", ["Bandra", "Juhu"]]
];
Accessing Nested Elements:
const data = [1, [2, 3], [4, [5, 6]]];
// Access first level
console.log(data[0]); // 1
console.log(data[1]); // [2, 3]
console.log(data[2]); // [4, [5, 6]]
// Access second level
console.log(data[1][0]); // 2
console.log(data[1][1]); // 3
console.log(data[2][1]); // [5, 6]
// Access third level
console.log(data[2][1][0]); // 5
console.log(data[2][1][1]); // 6
Levels of Nesting:
// Level 0: No nesting
const level0 = [1, 2, 3];
// Level 1: One level of nesting
const level1 = [1, [2, 3], 4];
// Level 2: Two levels of nesting
const level2 = [1, [2, [3, 4]], 5];
// Level 3: Three levels of nesting
const level3 = [1, [2, [3, [4, 5]]], 6];
// Infinite: Deeply nested (many levels)
const infinite = [1, [2, [3, [4, [5, [6, [7, [8]]]]]]]];
2. Why Is Array Flattening Useful?
Flattening arrays is important for several real-world scenarios. Let's understand when and why you need it.
Problem 1: Working with Multiple Data Sources
// Getting data from multiple APIs
const api1Results = [1, 2, 3];
const api2Results = [4, 5, 6];
const api3Results = [7, 8, 9];
// Combining them creates nested structure
const combined = [api1Results, api2Results, api3Results];
console.log(combined); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// But we want a flat list for processing
const flattened = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(flattened);
Problem 2: Processing Hierarchical Data
// File system structure
const fileSystem = [
"readme.txt",
["src", ["main.js", "utils.js"]],
["public", ["index.html", "style.css"]]
];
// To process all files, we need to flatten
// Then we can list all: readme.txt, main.js, utils.js, index.html, style.css
Problem 3: Simplifying Complex Data
// User groups data from database
const userGroups = [
["Raj", "Priya"],
["Anil"],
["Sneha", "Vikram", "Arjun"]
];
// For sending emails to all users, we need flat list
const allUsers = ["Raj", "Priya", "Anil", "Sneha", "Vikram", "Arjun"];
Problem 4: Data Transformation
// Search results from multiple sources
const searchResults = [
["iPhone 12", "iPhone 13", "iPhone 14"], // From Amazon
["iPhone 11", "iPhone 12 Pro"], // From Flipkart
["iPhone X", "iPhone 13"] // From Snapdeal
];
// For displaying unified results, flatten first
const allResults = ["iPhone 12", "iPhone 13", "iPhone 14", "iPhone 11", ...];
Problem 5: Easier Iteration and Processing
// Nested structure - complex iteration
const nested = [[1, 2], [3, 4], [5, 6]];
for (const subArray of nested) {
for (const item of subArray) {
console.log(item);
}
}
// Flattened - simple iteration
const flat = [1, 2, 3, 4, 5, 6];
for (const item of flat) {
console.log(item);
}
flat.forEach(item => console.log(item));
flat.map(item => item * 2);
3. Concept of Array Flattening
Flattening means converting a nested array into a single-level array. Let's visualize this process.
Visual Representation:
Before Flattening (Nested):
[1, [2, 3], [4, [5, 6]], 7]
└─ Level 0: [1, 7]
└─ Level 1: [2, 3], [4, [5, 6]]
└─ Level 2: [5, 6]
After Flattening (Flat):
[1, 2, 3, 4, 5, 6, 7]
Step-by-Step Flattening:
// Original nested array
const original = [1, [2, 3], [4, [5, 6]], 7];
// Step 1: Open the first level
// [1, 2, 3, 4, [5, 6], 7]
// Step 2: Open the second level
// [1, 2, 3, 4, 5, 6, 7]
// Result: Completely flat array
Different Depths of Flattening:
const nested = [1, [2, [3, [4, 5]]], 6];
// Flatten 1 level
// Result: [1, 2, [3, [4, 5]], 6]
// Flatten 2 levels
// Result: [1, 2, 3, [4, 5], 6]
// Flatten completely (all levels)
// Result: [1, 2, 3, 4, 5, 6]
Key Concept: Depth
// Depth 0 (already flat)
[1, 2, 3, 4, 5];
// Depth 1
[1, [2, 3], 4]; // One level of nesting
// Depth 2
[1, [2, [3, 4]], 5]; // Two levels of nesting
// Depth 3 (or infinity/unlimited)
[1, [2, [3, [4, 5]]]]; // Three or more levels
// When flattening, you specify how many levels to flatten
4. Different Approaches to Flatten Arrays
There are many ways to flatten arrays in JavaScript. Let's explore the most common and practical approaches.
Approach 1: Using flat() Method (Modern ES2019)
The flat() method is the simplest and most modern way to flatten arrays.
// Flatten one level
const nested1 = [1, [2, 3], [4, 5]];
const flat1 = nested1.flat();
console.log(flat1); // [1, 2, 3, 4, 5]
// Flatten two levels
const nested2 = [1, [2, [3, 4]], [5, 6]];
const flat2 = nested2.flat(2);
console.log(flat2); // [1, 2, 3, 4, 5, 6]
// Flatten all levels (use Infinity)
const nested3 = [1, [2, [3, [4, 5]]]];
const flat3 = nested3.flat(Infinity);
console.log(flat3); // [1, 2, 3, 4, 5]
// Default is 1 level
const nested4 = [1, [2, 3], [4, [5, 6]]];
const flat4 = nested4.flat(); // Same as flat(1)
console.log(flat4); // [1, 2, 3, 4, [5, 6]]
Practical Examples:
// Example 1: Flatten search results
const searchResults = [
["iPhone 12", "iPhone 13"],
["Samsung S21"],
["Pixel 6"]
];
const allPhones = searchResults.flat();
console.log(allPhones); // ["iPhone 12", "iPhone 13", "Samsung S21", "Pixel 6"]
// Example 2: Combine user groups
const userGroups = [
["Raj", "Priya"],
["Anil", "Sneha", "Vikram"]
];
const allUsers = userGroups.flat();
console.log(allUsers); // ["Raj", "Priya", "Anil", "Sneha", "Vikram"]
// Example 3: Flatten deeply nested data
const locations = [
["India", ["Delhi", ["South Delhi"]]],
["USA", ["California"]]
];
const allLocations = locations.flat(Infinity);
console.log(allLocations);
// ["India", "Delhi", "South Delhi", "USA", "California"]
Approach 2: Using flatMap()
flatMap() combines map() and flat() in one operation.
const numbers = [1, 2, 3, 4, 5];
// Using map and then flat
const result1 = numbers.map(n => [n, n * 2]).flat();
console.log(result1); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]
// Using flatMap (more efficient)
const result2 = numbers.flatMap(n => [n, n * 2]);
console.log(result2); // [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]
// Both produce same result, but flatMap is cleaner
More flatMap Examples:
// Example 1: Duplicate each item
const items = ["a", "b", "c"];
const duplicated = items.flatMap(item => [item, item]);
console.log(duplicated); // ["a", "a", "b", "b", "c", "c"]
// Example 2: Filter and transform simultaneously
const products = [
{ name: "Laptop", inStock: true },
{ name: "Phone", inStock: false },
{ name: "Tablet", inStock: true }
];
const stockedProducts = products.flatMap(product =>
product.inStock ? [product.name] : []
);
console.log(stockedProducts); // ["Laptop", "Tablet"]
// Example 3: Expand each item
const words = ["hello", "world"];
const chars = words.flatMap(word => word.split(""));
console.log(chars);
// ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]
Approach 3: Using Spread Operator
The spread operator can flatten one level at a time.
// Flatten one level
const nested = [1, [2, 3], [4, 5]];
// Using spread multiple times
const flat1 = [].concat(...nested);
console.log(flat1); // [1, 2, 3, 4, 5]
// Or with spread in array literal
const flat2 = [...nested[0], ...nested[1], ...nested[2]];
console.log(flat2); // Works but not scalable
// For multiple levels, need to repeat
const nested2 = [1, [2, [3, 4]], 5];
const flat3 = [].concat(...nested2); // [1, 2, [3, 4], 5] - Only one level
Approach 4: Using Recursion
Recursion is a traditional approach that works for any depth.
// Recursive flattening function
function flattenRecursive(arr) {
const result = [];
for (const item of arr) {
if (Array.isArray(item)) {
// If item is an array, recursively flatten it
result.push(...flattenRecursive(item));
} else {
// If item is not an array, just add it
result.push(item);
}
}
return result;
}
// Test it
const nested = [1, [2, [3, [4, 5]]], 6];
const flattened = flattenRecursive(nested);
console.log(flattened); // [1, 2, 3, 4, 5, 6]
Another Recursive Approach:
function flattenSimple(arr) {
return arr.reduce((acc, val) =>
Array.isArray(val) ? acc.concat(flattenSimple(val)) : acc.concat(val),
[]
);
}
const nested = [1, [2, [3, 4]], 5];
console.log(flattenSimple(nested)); // [1, 2, 3, 4, 5]
Approach 5: Using forEach with Recursion
function flattenWithForeach(arr) {
const result = [];
arr.forEach(item => {
if (Array.isArray(item)) {
result.push(...flattenWithForeach(item));
} else {
result.push(item);
}
});
return result;
}
const nested = [1, [2, [3, 4]], 5];
console.log(flattenWithForeach(nested)); // [1, 2, 3, 4, 5]
Approach 6: Flatten with Depth Limit
function flattenWithDepth(arr, depth = 1) {
const result = [];
for (const item of arr) {
if (Array.isArray(item) && depth > 0) {
result.push(...flattenWithDepth(item, depth - 1));
} else {
result.push(item);
}
}
return result;
}
const nested = [1, [2, [3, [4, 5]]], 6];
console.log(flattenWithDepth(nested, 1)); // [1, 2, [3, [4, 5]], 6]
console.log(flattenWithDepth(nested, 2)); // [1, 2, 3, [4, 5], 6]
console.log(flattenWithDepth(nested, 3)); // [1, 2, 3, 4, 5, 6]
console.log(flattenWithDepth(nested, Infinity)); // [1, 2, 3, 4, 5, 6]
Comparison of Approaches:
| Approach | Pros | Cons |
|---|---|---|
| flat() | Simple, modern, readable | Only works in modern browsers |
| flatMap() | Combines map and flat | Limited use cases |
| Spread + concat | Simple for one level | Only flattens one level |
| Recursion | Works for any depth | Can be slower for large arrays |
| Custom function | Full control, reusable | More code to write |
5. Real-World Scenarios and Interview Questions
Scenario 1: Flatten Search Results (Common Interview)
// Problem: Combine search results from multiple platforms
const searchResults = {
amazon: ["iPhone 12", "iPhone 13"],
flipkart: ["Samsung S21", "Samsung S22"],
myntra: ["OnePlus 9"]
};
// Solution
function getAllSearchResults(results) {
return Object.values(results).flat();
}
const allProducts = getAllSearchResults(searchResults);
console.log(allProducts);
// ["iPhone 12", "iPhone 13", "Samsung S21", "Samsung S22", "OnePlus 9"]
Scenario 2: Flatten Comment Threads (Common in Social Media)
// Problem: Flatten nested comment threads
const comments = [
{
id: 1,
text: "Great post!",
replies: [
{ id: 2, text: "Thanks!" },
{ id: 3, text: "I agree" }
]
},
{
id: 4,
text: "Not sure about this",
replies: [
{ id: 5, text: "Why?" }
]
}
];
// Solution
function getAllComments(comments) {
return comments.flatMap(comment => [
comment,
...getAllComments(comment.replies || [])
]);
}
const allComments = getAllComments(comments);
console.log(allComments.map(c => c.text));
// ["Great post!", "Thanks!", "I agree", "Not sure about this", "Why?"]
Scenario 3: Flatten Permissions (Common in Auth Systems)
// Problem: User has roles with permissions, flatten to get all permissions
const user = {
id: 1,
name: "Ashish",
roles: [
{
name: "Admin",
permissions: ["read", "write", "delete"]
},
{
name: "Editor",
permissions: ["read", "write"]
}
]
};
// Solution
function getAllPermissions(user) {
return user.roles
.map(role => role.permissions)
.flat();
}
const permissions = getAllPermissions(user);
console.log(permissions); // ["read", "write", "delete", "read", "write"]
// Remove duplicates
const uniquePermissions = [...new Set(permissions)];
console.log(uniquePermissions); // ["read", "write", "delete"]
Scenario 4: Flatten Tree Structure (Common in DOM and Data Trees)
// Problem: Flatten a tree structure
const organizationChart = {
name: "CEO",
employees: [
{
name: "Manager 1",
employees: [
{ name: "Developer 1" },
{ name: "Developer 2" }
]
},
{
name: "Manager 2",
employees: [
{ name: "Designer 1" }
]
}
]
};
// Solution
function flattenTree(node) {
const result = [node.name];
if (node.employees && node.employees.length > 0) {
node.employees.forEach(employee => {
result.push(...flattenTree(employee));
});
}
return result;
}
const allEmployees = flattenTree(organizationChart);
console.log(allEmployees);
// ["CEO", "Manager 1", "Developer 1", "Developer 2", "Manager 2", "Designer 1"]
Scenario 5: Interview Question - Flatten Unknown Depth
// Problem: Flatten an array of unknown depth
function flattenAny(arr) {
return arr.reduce((flat, item) =>
Array.isArray(item) ? flat.concat(flattenAny(item)) : flat.concat(item),
[]
);
}
// Test
const test1 = [1, [2, 3], [4, [5, 6]]];
console.log(flattenAny(test1)); // [1, 2, 3, 4, 5, 6]
const test2 = [[[1]], [[2]], [[[3]]]];
console.log(flattenAny(test2)); // [1, 2, 3]
const test3 = [1, [2, [3, [4, [5]]]]];
console.log(flattenAny(test3)); // [1, 2, 3, 4, 5]
Scenario 6: Flatten with Filtering
// Problem: Flatten and filter in one operation
const data = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// Get all numbers greater than 4
const filtered = data.flatMap(subArray =>
subArray.filter(num => num > 4)
);
console.log(filtered); // [5, 6, 7, 8, 9]
Scenario 7: Common Interview Trick - What if Array Contains null?
const nested = [1, [2, null, 3], [4, undefined, 5]];
// flat() keeps null and undefined
console.log(nested.flat()); // [1, 2, null, 3, 4, undefined, 5]
// If you want to remove them
const cleaned = nested.flat().filter(item => item != null);
console.log(cleaned); // [1, 2, 3, 4, 5]
// Or during flattening
function flattenClean(arr) {
return arr.reduce((flat, item) =>
Array.isArray(item)
? flat.concat(flattenClean(item))
: item != null ? flat.concat(item) : flat,
[]
);
}
console.log(flattenClean(nested)); // [1, 2, 3, 4, 5]
6. Performance Comparison
Let's compare different flattening approaches for performance.
// Create a test array
const createTestArray = (levels) => {
if (levels === 0) return 1;
return [createTestArray(levels - 1), createTestArray(levels - 1)];
};
const testArray = createTestArray(5);
// Test 1: Using flat()
console.time("flat()");
const result1 = testArray.flat(Infinity);
console.timeEnd("flat()");
// Test 2: Using recursive function
function flatRecursive(arr) {
return arr.reduce((flat, item) =>
Array.isArray(item) ? flat.concat(flatRecursive(item)) : flat.concat(item),
[]
);
}
console.time("Recursive");
const result2 = flatRecursive(testArray);
console.timeEnd("Recursive");
// Modern flat() is usually faster because it's optimized by the engine
Performance Tips:
// Good: Use flat() when available - it's optimized
const flat1 = array.flat(Infinity);
// Avoid: concat with spread in loop - creates many intermediate arrays
let result = [];
for (const item of array) {
result = [...result, ...item]; // Inefficient
}
// Better: Use reduce
const flat2 = array.reduce((flat, item) =>
flat.concat(item), []
);
// Best: Use flat()
const flat3 = array.flat();
7. Visual Flattening Process
Step-by-Step Visualization:
Original Array:
[1, [2, 3], [4, [5, 6]], 7]
Level 0 (input):
┌─ 1
├─ [2, 3]
├─ [4, [5, 6]]
└─ 7
First Pass (flatten 1 level):
┌─ 1
├─ 2
├─ 3
├─ 4
├─ [5, 6]
└─ 7
Second Pass (flatten 2 levels):
┌─ 1
├─ 2
├─ 3
├─ 4
├─ 5
├─ 6
└─ 7
Result:
[1, 2, 3, 4, 5, 6, 7]
Recursion Visualization:
flattenRecursive([1, [2, [3, 4]], 5])
├─ Process 1 → add to result
├─ flattenRecursive([2, [3, 4]])
│ ├─ Process 2 → add to result
│ └─ flattenRecursive([3, 4])
│ ├─ Process 3 → add to result
│ └─ Process 4 → add to result
└─ Process 5 → add to result
Result: [1, 2, 3, 4, 5]
8. Edge Cases to Handle
Edge Case 1: Empty Arrays
const empty = [];
console.log(empty.flat()); // []
const withEmpty = [1, [], 2, []];
console.log(withEmpty.flat()); // [1, 2]
Edge Case 2: Non-Array Elements
const mixed = [1, "hello", true, { key: "value" }];
console.log(mixed.flat()); // [1, "hello", true, { key: "value" }]
// Already flat, nothing changes
Edge Case 3: Null and Undefined
const withNulls = [1, [null, 2], [undefined, 3]];
console.log(withNulls.flat()); // [1, null, 2, undefined, 3]
// null and undefined are preserved
Edge Case 4: Very Deep Nesting
const veryDeep = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]];
console.log(veryDeep.flat()); // Only flattens 1 level
// [1, 2, [3, [4, [5, [6, [7, [8, [9, [10]]]]]]]]]
console.log(veryDeep.flat(Infinity)); // Flattens all levels
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Edge Case 5: Sparse Arrays (Arrays with Holes)
const sparse = [1, , 3]; // Note the missing element
console.log(sparse.length); // 3
console.log(sparse); // [1, <empty>, 3]
const nested = [1, [, 2], 3];
console.log(nested.flat());
// [1, 2, 3] - Empty slots are removed
9. Best Practices and Recommendations
Best Practice 1: Use flat() for Simple Cases
// Good: Clear and modern
const result = array.flat();
const deepResult = array.flat(Infinity);
// Avoid if not needed: Making it complex
const manual = array.reduce((flat, item) =>
Array.isArray(item) ? flat.concat(item) : flat.concat(item),
[]
);
Best Practice 2: Use flatMap() When Transforming
// Good: flatMap combines operations
const doubled = [1, 2, 3].flatMap(n => [n, n * 2]);
// Not as good: Separate map and flat
const doubled2 = [1, 2, 3].map(n => [n, n * 2]).flat();
Best Practice 3: Specify Depth Explicitly
// Good: Clear what you're doing
const flat1 = array.flat(1); // Exactly 1 level
const flat2 = array.flat(2); // Exactly 2 levels
// Avoid: Magic number without context
const flat3 = array.flat(5); // Why 5? Not clear
Best Practice 4: Handle Edge Cases
// Good: Consider what data you're receiving
function flattenData(arr) {
if (!Array.isArray(arr)) {
return [];
}
return arr.flat(Infinity).filter(item => item != null);
}
// Test with various inputs
console.log(flattenData(null)); // []
console.log(flattenData([1, null, 2])); // [1, 2]
Best Practice 5: Consider Performance
// For large deeply nested arrays, be careful
const huge = generateLargeNestedArray();
// Good: Don't over-flatten
const result1 = huge.flat(2); // Only flatten 2 levels
// Avoid: Always flattening completely might be slow
const result2 = huge.flat(Infinity); // Might be slow
10. Complete Examples
Example 1: Database Query Results
// Simulate API returning multiple result sets
const apiResponses = [
[
{ id: 1, name: "Raj" },
{ id: 2, name: "Priya" }
],
[
{ id: 3, name: "Anil" },
{ id: 4, name: "Sneha" }
]
];
// Flatten to get all users
const allUsers = apiResponses.flat();
console.log(allUsers);
// [
// { id: 1, name: "Raj" },
// { id: 2, name: "Priya" },
// { id: 3, name: "Anil" },
// { id: 4, name: "Sneha" }
// ]
// Further processing
const names = allUsers.map(user => user.name);
console.log(names); // ["Raj", "Priya", "Anil", "Sneha"]
Example 2: File System Listing
// Directory structure
const directories = {
src: [
"main.js",
"utils.js",
{ components: ["Button.js", "Card.js"] }
],
public: ["index.html", "favicon.ico"],
tests: ["main.test.js", "utils.test.js"]
};
// Get all files (simple case)
function getAllFiles(obj) {
return Object.values(obj).flat();
}
console.log(getAllFiles(directories));
// ["main.js", "utils.js", {components: [...]}, "index.html", "favicon.ico", "main.test.js", "utils.test.js"]
Example 3: Combine Multiple Lists
// Different user categories
const activeUsers = ["Raj", "Priya"];
const inactiveUsers = ["Anil", "Sneha"];
const newUsers = ["Vikram", "Arjun"];
// Combine all
const allUserLists = [activeUsers, inactiveUsers, newUsers];
const allUsers = allUserLists.flat();
console.log(allUsers);
// ["Raj", "Priya", "Anil", "Sneha", "Vikram", "Arjun"]
// Count total
console.log(allUsers.length); // 6
Conclusion
Array flattening is a fundamental technique in JavaScript that helps you work with nested data structures. Here's what we've learned:
Key Concepts:
Nested arrays contain other arrays as elements
Flattening converts nested structures to single-level arrays
Different approaches suit different scenarios
Main Methods:
flat(): Simplest and most modernflatMap(): Best when combining map and flattenRecursion: Works for any depth
Spread operator: Good for one level
When to Flatten:
Combining data from multiple sources
Processing hierarchical data
Simplifying for iteration and processing
Preparing data for display or storage
Best Practices:
Use
flat()for modern codeSpecify depth explicitly
Handle edge cases (null, undefined, sparse arrays)
Consider performance for large arrays
Use
flatMap()when transforming
Master array flattening, and you'll handle hierarchical and nested data with confidence. Whether it's in interviews, API responses, or database queries, this skill is invaluable for professional JavaScript development.




