0

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:

  1. acc = acc.concat(flatWithRec(item)); why accumulator is being reassign? how is it possible in reduce function? and why use concat here?

  2. return acc; why acc is being returned? is it a new Flat Array each time function is called?

  3. 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])
Aim
  • 1
  • 2
  • Try using a loop instead of `reduce` if that makes it easier to understand (or at least help figuring out what the `reduce` does) – Bergi Apr 17 '21 at 13:00
  • 1
    `acc.concat(..)` returns a new array and doesn't mutate the original array `acc`. So, they are reassigning. You could do it without reassigning using `push` like this `acc.push(...flatWithRec(item))` – adiga Apr 17 '21 at 13:00
  • And the accumulator needs to returned in `reduce` callback. It's how reduce works. – adiga Apr 17 '21 at 13:01

4 Answers4

0
  1. 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.concat returns 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/concat

  2. You need to return the accumulator at the end of each loop for the new value to be taken in account at the next loop.

  3. Javascript recursive array flattening

Jason Van Malder
  • 587
  • 1
  • 5
  • 15
0
  1. acc = acc.concat(flatWithRec(item)); why accumulator is being reassign? how is it possible in reduce function? and why use concat here?

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.

  1. 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.

  1. 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])
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
0

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 in reduce function? And why use concat here?

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; Why acc is 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]]]]));
Som Shekhar Mukherjee
  • 4,701
  • 1
  • 12
  • 28
0

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)));
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61