How to use Array map with accumulator?
Lets have a list of numbers and find a list of current sums. Example:
const nums = [1, 1, 1, -1, -1];
const sums = [1, 2, 3, 2, 1];
I try to do it with map
, by using accumulator in a thisArg
because according to: MDN Array.prototype.map()
thisArg
- value to use asthis
when executing callback.
I provide an object with acc
set to 0
as thisArg
:
const actual = nums.map(val => this.acc += val, {acc: 0});
require('assert').deepEqual(actual, sums);
It crashes with error:
AssertionError: [ 1, 2, 3, 2, 1 ] deepEqual [ NaN, NaN, NaN, NaN, NaN ]
The test passes with an external accumulator:
let acc = 0;
const actual = nums.map(val => acc += val);
accumulator is the value returned from the previous iteration. It will be initialValue for the first iteration. currentValue is array item in the current iteration.
Filtering first, and then mapping, can be accomplished in one line! In this example, we filter by items that include the word “coffee” and then map around the filtered array to create an array with only the names. It's a great way to make code look cleaner and operate more efficiently. We could do map and then filter!
Using reduce and map() together Here's how it's done: Map the array into an array of zeros and ones. Reduce the array of zeros and ones into the sum.
The syntax for the map() method is as follows: arr. map(function(element, index, array){ }, this); The callback function() is called on each array element, and the map() method always passes the current element , the index of the current element, and the whole array object to it.
With using arrow functions, you loose this
in the function, which is already set from the outer space.
You could use a function statement and thisArg
.
const nums = [1, 1, 1, -1, -1];
const actual = nums.map(function (val) { return this.acc += val; }, { acc: 0 });
console.log(actual);
For keeping a arrow function, you could use a closure over the accumulator,
(acc => val => acc += val)(0) // complete closure with callback
which works in two steps, first it calls the function directly with a value for acc
(acc => )(0) // function for generating a closure over acc
and returns the inner function as a callback for Array#map
val => acc += val // final callback
with a closure over acc
, that means the scope of acc
is inside of the own function and inside of the returned callback.
const nums = [1, 1, 1, -1, -1];
const actual = nums.map((acc => val => acc += val)(0));
console.log(actual);
You can use Array.prototype.reduce()
instead.
This will not require you to create additional closure for arrow function and provide accumulator as regular argument.
const nums = [1, 1, 1, -1, -1]
const actual = nums.reduce(
(acc, val) => (acc.push((acc[acc.length-1] || 0) + val), acc), []
)
console.log(actual) // [1, 2, 3, 2, 1]
Edit:
If you concerned about performance, here is a jsPerf for comparison with your's and @Nina's options:
.map()
with standard function - 27% slower in Firefox and 5% faster in Chrome than .map()
with factory function.reduce()
is 94% slower in Firefox and 52% slower in Chrome than fastest of two above..map()
with standard function!If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With