Berg_Durden
Berg_Durden

Reputation: 1781

I need some help to understand this combination of methods (reduce, concat and map)

I am studying ES6 and I am strugling with this line of code. I know I am losing something here and I just can't understand what is really happening.

The code:

const powerset = arr => 
                arr.reduce((a, v) => 
                    a.concat(a.map(r => 
                        [v].concat(r))), [[]]);

console.log(powerset([1, 2, 3]));

The output:

[[], [1], [2], [2, 1], [3], [3, 1], [3, 2], [3, 2, 1]]

What do I see here?

the first concat will concatenate inside the 'primary array' the return of each map, and this one will concatenate the value of r with the value of v, and I believe that r is equal to a.

Based on that what I understand is that (I know I am wrong, but I don't know why) it should work like this:

In the first 'level' a is an empty array and v is equal to 1, so the first value should be [1] and not [], since r is being concatenate with v; in the second 'level' a is equal to 1 and v to 2, what would return [2, 1] and in the third 'level' the return would be [3, 2, 1] since v is equal to 3 and a to [2, 1].

As I said before, I know I am wrong but I just can't see what I am losing here. I made my research, as well as a lot of experiments, but I didn't get it.

How is this code really working?

Upvotes: 0

Views: 49

Answers (1)

deceze
deceze

Reputation: 522125

Let's first fix that formatting a bit:

arr.reduce(
  (a, v) => a.concat(a.map(r => [v].concat(r))),
  [[]]
)

So, reduce takes [[]] as the starting value, and the callback returns this list concatenated with something else. So far so good, makes sense that the return value is [[], ...] then, it's the starting value with additional values appended.

With three values being passed into powerset, there will be three iterations of this reduce process.

Now, what is being concatenated to that list each turn?

a.map(r => [v].concat(r))

a is that list that it starts with and that will be returned, v is the current value from arr, the list that was passed into powerset to begin with. r is each value currently in a.

So, on the first iteration, a is [[]], so r will be [] once, and v is 1:

  [[]].map(_ => [1].concat([]))
→ [[]].map(_ => [1])  // [1].concat([]) is [1]
→ [[1]]

So this first map operation returns [[1]]:

  (a, _) => a.concat([[1]])
→ (_, _) => [[], [1]]

So, you're indeed seeing the beginning of the output here. On the next iteration, a is [[], [1]] and v is 2.

  a        .map(r => [v].concat(r))
→ [[], [1]].map(r => [2].concat(r))  // two mappings here:
  → []  → [2].concat([])             // [2]
  → [1] → [2].concat([1])            // [2, 1]
→ [[2], [2, 1]]

So:

  (a, _) => a.concat([[2], [2, 1]])
→ (_, _) => [[], [1], [2], [2, 1]]

And you can figure out the third iteration yourself.

Upvotes: 1

Related Questions