Javascript recursive array flattening

41,482

Solution 1

The problem is how you are passing the processing of array, if the value is an array then you are keep calling it causing an infinite loop

function flatten() {
    var flat = [];
    for (var i = 0; i < arguments.length; i++) {
        if (arguments[i] instanceof Array) {
            flat.push.apply(flat, flatten.apply(this, arguments[i]));
        } else {
            flat.push(arguments[i]);
        }
    }
    return flat;
}

Demo: Fiddle

Here's a more modern version:

function flatten(items) {
  const flat = [];

  items.forEach(item => {
    if (Array.isArray(item)) {
      flat.push(...flatten(item));
    } else {
      flat.push(item);
    }
  });

  return flat;
}

Solution 2

The clean way to flatten an Array in 2019 with ES6 is flat():

const array = [1, 1, [2, 2], [[3, [4], 3], 2]]

// All layers
array.flat(Infinity) // [1, 1, 2, 2, 3, 4, 3, 2]

// Varying depths
array.flat() // [1, 1, 2, 2, Array(3), 2]

array.flat(2) // [1, 1, 2, 2, 3, Array(1), 3, 2]
array.flat().flat() // [1, 1, 2, 2, 3, Array(1), 3, 2]

array.flat(3) // [1, 1, 2, 2, 3, 4, 3, 2]
array.flat().flat().flat() // [1, 1, 2, 2, 3, 4, 3, 2]

Mozilla Docs

Can I Use - 94% Dec '21

Solution 3

If the item is array, we simply add all the remaining items to this array

function flatten(array, result) {
  if (array.length === 0) {
    return result
  }
  var head = array[0]
  var rest = array.slice(1)
  if (Array.isArray(head)) {
    return flatten(head.concat(rest), result)
  }
  result.push(head)
  return flatten(rest, result)
}

console.log(flatten([], []))
console.log(flatten([1], []))
console.log(flatten([1,2,3], []))
console.log(flatten([1,2,[3,4]], []))
console.log(flatten([1,2,[3,[4,5,6]]], []))
console.log(flatten([[1,2,3],[4,5,6]], []))
console.log(flatten([[1,2,3],[[4,5],6,7]], []))
console.log(flatten([[1,2,3],[[4,5],6,[7,8,9]]], []))

Solution 4

[...arr.toString().split(",")]

Use the toString() method of the Object. Use a spread operator (...) to make an array of string and split it by ",".

Example:

let arr =[["1","2"],[[[3]]]]; // output : ["1", "2", "3"]

Solution 5

A Haskellesque approach...

function flatArray([x,...xs]){
  return x !== undefined ? [...Array.isArray(x) ? flatArray(x) : [x],...flatArray(xs)]
                         : [];
}

var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10],
    fa = flatArray(na);
console.log(fa);

So i think the above code snippet could be made easier to understand with proper indenting;

function flatArray([x,...xs]){
  return x !== undefined ? [ ...Array.isArray(x) ? flatArray(x)
                                                 : [x]
                           , ...flatArray(xs)
                           ]
                         : [];
}

var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10],
    fa = flatArray(na);
console.log(fa);
Share:
41,482
yxfxmx
Author by

yxfxmx

Updated on July 09, 2022

Comments

  • yxfxmx
    yxfxmx almost 2 years

    I'm exercising and trying to write a recursive array flattening function. The code goes here:

    function flatten() {
        var flat = [];
        for (var i = 0; i < arguments.length; i++) {
            if (arguments[i] instanceof Array) {
                flat.push(flatten(arguments[i]));
            }
            flat.push(arguments[i]);
        }
        return flat;
    }
    

    The problem is that if I pass there an array or nested arrays I get the "maximum call stack size exceeded" error. What am I doing wrong?