I have found this interesting code example which flattens an array using the recursion and reduce function instead of flat. I get what it does except the following:
acc = acc.concat(flatWithRec(item));why accumulator is being reassign? how is it possible in reduce function? and why useconcathere?return acc;why acc is being returned? is it a new Flat Array each time function is called?is there a way to, still, use recursion and make it easier for the reader to understand?
Please clarify
function flatWithRec(arr) {
const flatArray = arr.reduce((acc, item) => {
if (Array.isArray(item)) {
acc = acc.concat(flatWithRec(item));
} else {
acc.push(item);
}
return acc;
}, []);
return flatArray;
}
console.log(flatWithRec([1, [2, 3, [4],
[5, 6, [7]]
]]))
// output: [1, 2, 3, 4, 5, 6, 7])
4 Answers 4
The accumulator is an array. You reassign it to give it the new array containing the items of the one you have at the beginning of the loop and the items of the array items to add. As said in the comments,
acc.concatreturns a new array containing the items of the arrays passed in parameter. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concatYou need to return the accumulator at the end of each loop for the new value to be taken in account at the next loop.
Comments
acc = acc.concat(flatWithRec(item));why accumulator is being reassign? how is it possible in reduce function? and why useconcathere?
The accumulator (acc) is a function argument, and can be re-assigned, although it's not a good practice to do so. Concat combines two items (array or otherwise), and returns a new array, so you need to assign it to the acc as the result of the current loop.
return acc;why acc is being returned? is it a new Flat Array each time function is called?
The accumulator holds the current state of the reduced items, in your case the current flat array after each loop. You need to return it, so the next loop can continue to accumulate.
- is there a way to, still, use recursion and make it easier for the reader to understand?
My take on flatWithRec - always concat, but if it's an array call flatWithRec on it before concatenating:
function flatWithRec(arr) {
return arr.reduce((acc, item) =>
acc.concat(
Array.isArray(item)
? flatWithRec(item)
: item
), []);
}
const result = flatWithRec([1, [2, 3, [4], [5, 6, [7]]]])
console.log(result) // output: [1, 2, 3, 4, 5, 6, 7])
Comments
So, the callback for the reduce method runs for every item in the array and whatever is returned from iteration x is passed as the first argument to iteration x+1. So, it is essential to make sure that during every iteration the correct state is returned.
acc = acc.concat(flatWithRec(item))Why accumulator is being reassigned? How is it possible inreducefunction? And why useconcathere?
So, we are assigning acc the return value of concat because concat does not change the original array, it returns a fresh array. Accumulator is just like any other parameter so you can reassign. Not at all necessary to use concat (see my solution at the end using push and spread).
return acc;Whyaccis being returned? Is it a new flat array each time the function is called?
As, already mentioned you need to return the correct state from the callback. And yes, during each iteration a new array is being created (which is not that performant, see my solution at the end)
Is there a way to still use recursion and make it easier for the reader to understand?
Easier for reader I can't tell, but here's my solution using => functions and spread.
const flatWithRec = (arr) =>
arr.reduce((acc, item) => (
Array.isArray(item) ? acc.push(...flatWithRec(item)) : acc.push(item), acc
), []);
console.log(flatWithRec([1, [2, 3, [4], [5, 6, [7]]]]));
Comments
Yes, there is a way to use recursion and make it easier to understand:
function f(A, i=0){
return i == A.length ? [] : (Array.isArray(A[i]) ? f(A[i]) : [A[i]]).concat(f(A, i+1));
}
var A = [1, [2, 3, [4], [5, 6, [7]]]];
console.log(JSON.stringify(A));
console.log(JSON.stringify(f(A)));
reduceif that makes it easier to understand (or at least help figuring out what thereducedoes)acc.concat(..)returns a new array and doesn't mutate the original arrayacc. So, they are reassigning. You could do it without reassigning usingpushlike thisacc.push(...flatWithRec(item))reducecallback. It's how reduce works.