Reputation: 1781
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
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