# Array Flatten in JavaScript

Arrays in JavaScript can hold anything: numbers, strings, objects, and even other arrays. That last part is where things get interesting. When an array contains other arrays as its elements, you end up with a nested structure that can go as deep as you like. Understanding how to work with that structure, and specifically how to flatten it, is one of those foundational skills that shows up both in real codebases and on whiteboards during interviews.

* * *

## What Nested Arrays Are

A nested array is simply an array that contains one or more arrays as elements. Consider this:

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

The outer array has three elements. The first is the number `1`. The second is another array `[2, 3]`. The third is an array `[4, [5, 6]]`, which itself contains another array inside it.

Nesting can go arbitrarily deep:

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

Each level is an array living inside another array. Accessing a value at a deep level requires chaining multiple index lookups, which quickly becomes awkward. Working with the data as a whole, iterating through it, summing it, searching it, becomes complicated when the structure is uneven and unpredictable in depth.

This is where flattening comes in.

* * *

## Why Flattening Is Useful

Flattening converts a nested array into a single, flat array where all the values sit at the same level. The nested structure is dissolved, and what remains is a clean sequence of values.

```javascript
// Before flattening
[1, [2, 3], [4, [5, 6]]]

// After flattening one level
[1, 2, 3, 4, [5, 6]]

// After flattening completely
[1, 2, 3, 4, 5, 6]
```

This matters in practice more than it might seem. APIs often return data in nested shapes because the structure reflects relationships. After you have extracted what you need, you frequently want to work with a flat list. Data transformations, aggregations, and rendering a list of items on a page all become simpler when you are not fighting multiple levels of nesting.

It also comes up in function composition. Functions that produce arrays of arrays, like `map` applied to a function that itself returns an array, produce nested results that need flattening before the next step in your pipeline can work with them cleanly.

* * *

## The Concept of Flattening Step by Step

Before looking at code, it helps to think through what flattening actually does at each step.

Take this array:

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

Walk through each element:

*   `1` is a number, not an array. It goes directly into the result.
    
*   `[2, 3]` is an array. Take its elements out individually: `2` goes in, then `3` goes in.
    
*   `[4, [5, 6]]` is an array. Take its elements: `4` goes in, then `[5, 6]` is encountered.
    
*   `[5, 6]` is another array. If you are flattening completely, take its elements: `5` goes in, then `6` goes in.
    

Result: `[1, 2, 3, 4, 5, 6]`.

The logic is recursive in nature. For every element you encounter, you ask the same question: is this an array or is it a plain value? If it is a plain value, include it. If it is an array, step inside it and ask the same question again. You keep going until there are no more arrays left to step into.

This recursive thinking is the foundation of every flattening approach.

* * *

## Different Approaches to Flatten Arrays

**Using the built-in** `flat` **method**

JavaScript introduced `Array.prototype.flat` in ES2019. It is the simplest and most direct option.

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

arr.flat();      // [1, 2, 3, 4, [5, 6]] - one level by default
arr.flat(2);     // [1, 2, 3, 4, 5, 6]  - two levels deep
arr.flat(Infinity); // [1, 2, 3, 4, 5, 6]  - all levels, no matter how deep
```

Passing `Infinity` as the depth argument tells `flat` to keep going until the structure is completely flat. For unknown depths, this is the safest option.

Knowing this method exists is useful. But interviews rarely stop there. They want to know if you can build it yourself.

**Recursive approach**

The recursive approach maps most directly to the conceptual model described above. For each element, check if it is an array. If it is, flatten it recursively and spread the result. If it is not, include it as is.

```javascript
function flattenRecursive(arr) {
  let result = [];
  for (let element of arr) {
    if (Array.isArray(element)) {
      result = result.concat(flattenRecursive(element));
    } else {
      result.push(element);
    }
  }
  return result;
}
```

Walking through this with `[1, [2, 3], [4, [5, 6]]]`:

*   `1` is not an array. Push it. Result: `[1]`.
    
*   `[2, 3]` is an array. Call `flattenRecursive([2, 3])`.
    
    *   `2` is not an array. Push it.
        
    *   `3` is not an array. Push it.
        
    *   Returns `[2, 3]`.
        
*   Concat `[2, 3]` onto the result. Result: `[1, 2, 3]`.
    
*   `[4, [5, 6]]` is an array. Call `flattenRecursive([4, [5, 6]])`.
    
    *   `4` is not an array. Push it.
        
    *   `[5, 6]` is an array. Call `flattenRecursive([5, 6])`.
        
        *   Returns `[5, 6]`.
            
    *   Concat. Returns `[4, 5, 6]`.
        
*   Concat `[4, 5, 6]` onto the result. Result: `[1, 2, 3, 4, 5, 6]`.
    

Each recursive call handles one level. The call stack naturally tracks where you are in the nesting.

**Using reduce and recursion**

A more compact version uses `reduce` to accumulate results:

```javascript
function flattenReduce(arr) {
  return arr.reduce((acc, element) => {
    return Array.isArray(element)
      ? acc.concat(flattenReduce(element))
      : acc.concat(element);
  }, []);
}
```

Same logic, different shape. `reduce` starts with an empty array and builds up the result as it moves through each element. If the element is an array, it recursively flattens it first. If not, it adds it directly.

**Iterative approach with a stack**

Recursive solutions are elegant but can hit call stack limits on extremely deeply nested structures. An iterative approach using an explicit stack avoids this:

```javascript
function flattenIterative(arr) {
  const stack = [...arr];
  const result = [];

  while (stack.length > 0) {
    const element = stack.pop();
    if (Array.isArray(element)) {
      stack.push(...element);
    } else {
      result.push(element);
    }
  }

  return result.reverse();
}
```

Instead of relying on the call stack, you manage your own stack. Pop an element. If it is an array, push its contents back onto the stack for processing. If it is a value, add it to the result. Because popping from the end and pushing back onto the end reverses the order, a final `reverse()` restores the original left-to-right order.

This is less immediately readable than the recursive version but demonstrates a deeper understanding of the underlying mechanics, which is exactly what makes it worth knowing.

**Flattening only one level**

Sometimes you do not want to flatten everything, just collapse one level of nesting. This comes up when working with data where the outer grouping means something and you only want to dissolve one layer:

```javascript
function flattenOne(arr) {
  return arr.reduce((acc, element) => acc.concat(element), []);
}
```

No `Array.isArray` check, no recursion. `concat` already handles both arrays and non-arrays naturally, so passing each element through `concat` dissolves exactly one level of nesting without going deeper.

* * *

## Common Interview Scenarios

Flattening problems appear in interviews in a few predictable forms.

The most direct is: implement `flat` without using it. This is a test of whether you understand the recursive structure of nested arrays and can translate that understanding into code. The recursive approach with `Array.isArray` is the expected answer.

A follow-up might be: what if the nesting is extremely deep, thousands of levels? This is pointing at the call stack limitation of recursion and asking whether you know the iterative alternative.

Another variation: flatten only up to a specific depth. This requires adding a depth parameter to your recursive function and decrementing it with each level:

```javascript
function flattenDepth(arr, depth = 1) {
  return arr.reduce((acc, element) => {
    if (Array.isArray(element) && depth > 0) {
      return acc.concat(flattenDepth(element, depth - 1));
    }
    return acc.concat(element);
  }, []);
}
```

When `depth` reaches zero, the check short-circuits and arrays at that level are added as-is rather than stepped into. This mirrors exactly how `flat(n)` works.

Interviewers also sometimes frame flattening as part of a larger problem. You might be asked to sum all values in a nested structure, find a specific value across all levels, or count how many elements exist across all nesting levels. Recognizing that these all reduce to the same walk-every-element-recursively pattern is the key insight.

* * *

## Wrapping Up

Nested arrays are a natural consequence of working with data that has structure. Flattening is the tool that lets you collapse that structure when the nesting is no longer useful and you just need a clean sequence of values.

The built-in `flat` method handles most real-world cases with a single line. Understanding how to implement it yourself, recursively and iteratively, with adjustable depth, builds the kind of problem-solving intuition that extends well beyond this specific operation. The recursive thinking behind array flattening shows up constantly: in tree traversal, in deeply nested objects, in any problem where the structure can nest arbitrarily and you need to visit every element.

The pattern is always the same. Ask what each element is. If it is a container, step inside and ask again. If it is a value, use it. Keep going until there is nothing left to step into.
