Can anyone show me an iterative solution for the following problem? I solved it recursively but struggled with an iterative solution. (Facebook Technical Interv
A fairly concise, readable algorithm:
function flatten(input) {
var output = [];
var todo = [input];
var current;
while(todo.length) {
var current = todo.shift();
if(Array.isArray(current)) {
todo.unshift.apply(todo, current)
} else {
output.push(current);
}
}
return output;
}
This version performs better than my other answer, but is still significantly slower than James Wilkins' answer.
JSBin
Tomalak's JSPerf
Works, but not recommended:
var flatten = function(input) {
return eval("[" + JSON.stringify(input).
replace(/\[/g,"").replace(/\]/g,"") + "]");
}
How about this?
inp = [1, {a: 2}, [3], [[4, 5], 6], 7]
out = inp;
while(out.some(Array.isArray))
out = [].concat.apply([], out);
document.write(JSON.stringify(out));
Here is one way:
var input = [1, {a: 2}, [3], [[4, 5], 6], 7];
function flatten(input) {
var i, placeHolder = [input], lastIndex = [-1], out = [];
while (placeHolder.length) {
input = placeHolder.pop();
i = lastIndex.pop() + 1;
for (; i < input.length; ++i) {
if (Array.isArray(input[i])) {
placeHolder.push(input);
lastIndex.push(i);
input = input[i];
i = -1;
} else out.push(input[i]);
}
}
return out;
}
flatten(input);
Explanation: If iterating over a nested structure, you just have to remember where you were before by saving the current array and position before moving into the nested array (this is usually taken care of via the stack for recursive solutions).
Note: If you reuse the arrays placeHolder
and lastIndex
you won't need to keep recreating them every time. Perhaps something like this:
var flatten = function(){
var placeHolder = [], lastIndex = [];
placeHolder.count = 0;
lastIndex.count = 0;
return function flatten(input) {
var i, out = [];
placeHolder[0] = input; placeHolder.count = 1;
lastIndex[0] = -1; lastIndex.count = 1;
while (placeHolder.count) {
input = placeHolder[--placeHolder.count];
i = lastIndex[--lastIndex.count] + 1;
for (; i < input.length; ++i) {
if (Array.isArray(input[i])) {
placeHolder[placeHolder.count++] = input;
lastIndex[lastIndex.count++] = i;
input = input[i];
i = -1;
} else out.push(input[i]);
}
}
return out;
}
}();
This is even faster again (for flat iteration that is), and less garbage collector issues calling it many times. The speed is very close to that of recursive function calling in Chrome, and many times faster than recursion in FireFox and IE.
I recreated Tomalak's tests here since the old jsPerf is broken for editing: https://jsperf.com/iterative-array-flatten-2
Here are two approaches, recursive and iterative and their comparison to Array.flat. Maybe it'll help someone
const arrayToFlatten = [[1], [2, [3]], null, [[{}]], undefined];
// takes an array and flattens it recursively, default depth is 1 (just like Array.flat())
function flattenRecursive(arr, depth = 1) {
let myArray = [];
if (depth === 0){ // if you've reached the depth don't continue
myArray = arr;
} else if(!Array.isArray(arr)) { // add item to array if not an array
myArray.push(arr);
} else { // flatten each item in the array then concatenate
arr.forEach(item => {
const someNewArray = flattenRecursive(item, depth - 1);
myArray = myArray.concat(someNewArray);
});
}
return myArray;
}
// takes an array and flattens it using a loop, default depth is 1 (just like Array.flat())
function flattenIterative(arr, depth = 1) {
let result = arr;
// if an element is an array
while(result.some(Array.isArray) && depth) {
// flatten the array by one level by concating an empty array and result using apply
result = [].concat.apply([], result);
depth--; // track depth
}
return result;
}
console.log(arrayToFlatten.flat(2)); // ES^
console.log(flattenRecursive(arrayToFlatten, 2));
console.log(flattenIterative(arrayToFlatten, 2));
Here's a solution that flattens in place.
function flatten(arr) {
var i = 0;
if (!Array.isArray(arr)) {
/* return non-array inputs immediately to avoid errors */
return arr;
}
while (i < arr.length) {
if (Array.isArray(arr[i])) {
arr.splice(i, 1, ...arr[i]);
} else {
i++;
}
}
return arr;
}
This solution iterates through the array, flattening each element one level of nesting at a time until it cannot be flattened any more.