Skip to main content

Command Palette

Search for a command to run...

๐Ÿš€ Mastering Array Flattening in JavaScript: From Built-ins to Custom Polyfills

Published
โ€ข7 min read
๐Ÿš€ Mastering Array Flattening in JavaScript: From Built-ins to Custom Polyfills

If you've spent any time working with JavaScript, you've probably encountered a situation where you had an array filled with other arrays, and you just wanted one clean, flat list.

Flattening arrays is one of those concepts that seems incredibly simple on the surface, but it has a funny way of showing up everywhereโ€”from everyday data transformation tasks to the most rigorous coding interviews.

In this guide, we're going to break down array flattening step-by-step. We'll explore why you might need it, the built-in ways to do it, and, most importantly, how to write your own custom flattening functions when the built-in tools aren't available or aren't enough.


๐Ÿ“ฆ Wait, What Are Nested Arrays?

Before we flatten anything, let's make sure we're on the same page. A nested array (or multidimensional array) is simply an array that contains other arrays as its elements.

Think of it like a set of nesting dolls or boxes within boxes.

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

If we were to map this out visually, it would look something like a tree of values:

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

Instead of a simple, flat list of numbers, our elements are grouped in sub-arrays at varying depths.


๐Ÿค” Why Do We Need to Flatten Them?

Flattening is the process of taking all those nested elements and extracting them into a single-level, flat array. Taking our previous example:

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

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

You might be wondering, "When would I actually need to do this in the real world?"

There are plenty of practical use cases:

  • Cleaning up API responses: Sometimes the data you receive from a backend is heavily nested and difficult to iterate over cleanly.

  • Working with complex forms: Handling inputs that have grouped values or sub-categories.

  • Component rendering in UI frameworks: Frameworks like React often need a flat list of items to render efficiently.

  • Handling file systems or tree-like data: Extracting a list of all files from a directory tree.

Think of flattening as taking everything out of those nested boxes and laying them all out side-by-side on a single table.


๐Ÿ” The Easy Way: Using Built-in .flat()

Modern JavaScript provides an incredibly convenient, built-in method for this: Array.prototype.flat().

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

console.log(arr.flat(2)); 
// Output: [1, 2, 3, 4, 5, 6]

How it works:

The .flat() method takes an optional argument called depth, which specifies how many levels of nesting you want to flatten.

  • By default, depth is 1. This means it will only flatten one level down.

  • If you have deeply nested arrays and want to completely flatten them, you can pass Infinity as the depth!

// Completely flatten an array of any depth
const deeplyNested = [1, [[[2]]]];
console.log(deeplyNested.flat(Infinity)); // [1, 2]

As straightforward as .flat() is, there's a catch. Interviewers love asking you to solve this problem without using the built-in method. Why? Because it tests your understanding of recursion, iteration, and data structures. Let's look at how to build it from scratch.


๐Ÿ” The Classic Interview Answer: Recursion

If you're in a technical interview and you're asked to flatten an array, this is generally the approach they are looking for. Recursion is perfect here because an array could potentially have smaller arrays inside it, which might have smaller arrays inside them... and so on.

function flattenArray(arr) {
  let result = [];

  for (let item of arr) {
    // If the current item is an array, we need to dig deeper!
    if (Array.isArray(item)) {
      result = result.concat(flattenArray(item)); // Recursive call
    } else {
      // If it's not an array, just add it to our result list
      result.push(item);
    }
  }

  return result;
}

Breaking it down:

  1. We iterate through every element in the array.

  2. We check if the element is an array itself using Array.isArray().

  3. If it is an array, we call our flattenArray function again on that sub-array. This is the recursion!

  4. Once we get the flattened result of that sub-array, we merge it into our main result array using .concat().

  5. If the element is just a regular value (like a number), we push it directly into the result.


๐Ÿงฉ The Functional Approach: Using reduce()

Functional programming concepts are highly valued in modern JS. We can rewrite our recursive function using reduce(...), which makes for a very clean and concise solution.

function flattenWithReduce(arr) {
  return arr.reduce((acc, currentItem) => {
    return acc.concat(
      Array.isArray(currentItem) ? flattenWithReduce(currentItem) : currentItem
    );
  }, []);
}

This functions exactly the same as the recursion approach above, but it uses the accumulator (acc) pattern. Whenever you have to turn an array into a single "accumulated" value (even if that single value is another array), reduce() is usually a great tool for the job.


โšก The Iterative Approach: Using a Stack

Recursion is elegant, but it can hit call stack limits with massive, heavily nested datasets. If you want to impress an interviewer, show them you can do it iteratively using a "Stack".

function flattenIterative(arr) {
  // We initialize our stack with the contents of the original array
  const stack = [...arr];
  const result = [];

  // While there are still items to process in the stack
  while (stack.length > 0) {
    const next = stack.pop();

    if (Array.isArray(next)) {
      // If it's an array, push its items back onto the stack to be processed
      stack.push(...next);
    } else {
      // If it's a final value, add it to the front of our result
      result.unshift(next);
    }
  }

  return result;
}

This approach manually manages the un-nesting process, avoiding the memory overhead of multiple function calls.


๐ŸŽฏ Bonus Challenge: Writing a Custom .flat(depth) Polyfill

Want to go the extra mile? Let's recreate the exact behavior of Array.prototype.flat(), including the depth parameter!

function customFlat(arr, depth = 1) {
  // Base case: If we've reached 0 depth, just return the current array
  if (depth === 0) return arr;

  return arr.reduce((acc, item) => {
    if (Array.isArray(item)) {
      // We found a sub-array! Flatten it, and decrease the remaining depth by 1
      return acc.concat(customFlat(item, depth - 1));
    }
    // It's a normal value, just append it
    return acc.concat(item);
  }, []);
}

console.log(customFlat([1, [2, [3, 4]]], 1)); // [1, 2, [3, 4]]
console.log(customFlat([1, [2, [3, 4]]], 2)); // [1, 2, 3, 4]

This is the ultimate interview flex. You're combining functional concepts, recursion, and API understanding all into one neat function.


๐Ÿ“‰ Watch out for these Edge Cases

When tackling these problems, remember to test your functions against weird edge cases:

  • Empty arrays: How does your function handle []?

  • Extreme deep nesting: Does it crash on [[[[[1]]]]]?

  • Mixed Data Types: Keep in mind that arrays can hold strings, null, booleans, and objects: [1, "hello", [true, [null, {}]]]

๐Ÿ Final Thoughts

Flattening arrays is much more than just a convenience utility or a trivia question. Mastering these custom implementations forces you to think clearly about trees, recursion, and how data structures relate to one another.

If you are currently preparing for an interview, here's my biggest tip: Close your editor, grab a piece of paper, and try to write the recursive array flattener from memory. Once you can explain it to an interviewer smoothly, you'll be one step closer to acing your JavaScript fundamentals! ๐Ÿš€

JavaScript Decoded

Part 2 of 9

JavaScript Decoded is a step-by-step blog series designed for beginners and self-taught developers who want to truly understand JavaScript from the ground up. Each post covers one core concept โ€” variables, control flow, functions, arrays, objects, and more โ€” with practical code examples, beginner-friendly explanations, and tips you can apply right away. No fluff, just clarity.

Up next

JavaScript Array Methods You Must Know

Ever feel like your JavaScript code is starting to look like a giant, messy plate of spaghetti? ๐Ÿ Weโ€™ve all been thereโ€”struggling with clunky for loops, manually managing index variables like i, and