As is commented by @Jonathan the problem is that you use a counter i
which is not declared in the function and thus global. As a result recursive calls will alter the i
of the caller, etc.
function steamrollArray(arr) {
// I'm a steamroller, baby
var flat = [];
for(var i=0; i < arr.length; i++ ){
if( Array.isArray(arr[i]) ){
flat = flat.concat(steamrollArray(arr[i]));
} else {
flat.push(arr[i]);
}
} // end of the for loop
return flat;
}
A second challenge however is making the code more efficient in both time and memory. This can be done by limiting the amount of list construction to one. You can do this by using a concept called an accumulator: a variable you update through the recursive process. First we need to initialize the variable:
function steamrollArray(arr) {
return steamer(arr,[]);
}
In this case the accumulator is the result as well, and we initialize the result as an empty array. Evidently we still need to implement the steamer
function:
function steamer (arr,target) {
if(Array.isArray(arr)) {
var n = arr.length;
for(var i = 0; i < n; i++) {
steamer(arr[i],target);
}
} else {
target.push(arr);
}
return target;
}
What one does is passing the target through the recursive enumeration of the array tree. In case the value turns out to be a scalar (Array.isArray
returns false
) we add the element to the end of the target
; otherwise we perform a recursive call.
The last thing this function does is returning the target
, after the initial steamer
call, target
will be filled with all elements in the nested list.
The advantage is we do not need expensive concat
functions, but only use the push
function O(n) times. If we make abstraction of the processing time necessary to construct an array (assume push
works in O(1) time), the algorithm now works in O(n) time and memory with n the number of leaves in the list tree.