How to flatten nested array in javascript?
This is an alternative to recursion (see jsfiddle here) and should accept any level of depth which avoids stack overflow.
var array = [[0, 1], [2, 3], [4, 5, [6, 7, [8, [9, 10]]]]];
console.log(flatten(array), array); // does not mutate array
console.log(flatten(array, true), array); // array is now empty
// This is done in a linear time O(n) without recursion
// memory complexity is O(1) or O(n) if mutable param is set to false
function flatten(array, mutable) {
var toString = Object.prototype.toString;
var arrayTypeStr = '[object Array]';
var result = [];
var nodes = (mutable && array) || array.slice();
var node;
if (!array.length) {
return result;
}
node = nodes.pop();
do {
if (toString.call(node) === arrayTypeStr) {
nodes.push.apply(nodes, node);
} else {
result.push(node);
}
} while (nodes.length && (node = nodes.pop()) !== undefined);
result.reverse(); // we reverse result to restore the original order
return result;
}
ES6-style with recursion:
Redacted
June 2018 Update:
There is now an ES proposal for an Array.prototype.flat
method. It is currently at stage 3, meaning it's likely to be implemented by browsers soon(ish) and make it into the spec in its current form. There are probably some polyfills floating around.
Example:
const nested = [[[0], [1]], [[2], [3]], [[4], [5]]];
const flattened = nested.flat(2); // Need to specify depth if > 1
June 2019 Update:
Array.prototype.flat
was officially added to the language in the ES2019 spec.
Perfect use case for recursion, which could handle even deeper structure:
function flatten(ary) {
var ret = [];
for(var i = 0; i < ary.length; i++) {
if(Array.isArray(ary[i])) {
ret = ret.concat(flatten(ary[i]));
} else {
ret.push(ary[i]);
}
}
return ret;
}
flatten([[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]]) // [0, 1, 2, 3, 4, 5]
Alternatively, as an Array method:
Array.prototype.flatten = function() {
var ret = [];
for(var i = 0; i < this.length; i++) {
if(Array.isArray(this[i])) {
ret = ret.concat(this[i].flatten());
} else {
ret.push(this[i]);
}
}
return ret;
};
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flatten() // [0, 1, 2, 3, 4, 5]
EDIT #1: Well, think it a little bit functional way (except for the named recursion which should be using Y-combinator for pure functional :D).
function flatten(ary) {
return ary.reduce(function(a, b) {
if (Array.isArray(b)) {
return a.concat(flatten(b))
}
return a.concat(b)
}, [])
}
Let's adopt some ES6 syntax which makes it even shorter, in one line.
const flatten = (ary) => ary.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [])
But remember, this one cannot be applied as an array method, because arrow functions don't have theirs own this
.
EDIT #2: With the latest Array.prototype.flat
proposal this is super easy. The array method accepts an optional parameter depth
, which specifies how deep a nested array structure should be flattened (default to 1
).
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat() // [[[[0]], [1]], [[[2], [3]]], [[4], [5]]]
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat(2) // [[[0]], [1], [[2], [3]], [4], [5]]
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat(3) // [[0], 1, [2], [3], 4, 5]
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat(4) // [0, 1, 2, 3, 4, 5]
So to flatten an array of arbitrary depth, just call flat
method with Infinity
.
[[[[[0]], [1]], [[[2], [3]]], [[4], [5]]]].flat(Infinity) // [0, 1, 2, 3, 4, 5]