Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
How do I loathe thee? Let me count the ways…
The Array.reduce() method has been around awhile. Like map(), filter() and find(), it operates upon an array of values by invoking a callback function.
Here’s an example from the developer.mozilla.org site:
const array1 = [1, 2, 3, 4];const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4console.log(array1.reduce(reducer));// expected output: 10
// 5 + 1 + 2 + 3 + 4console.log(array1.reduce(reducer, 5));// expected output: 15
Seems straightforward, right? On with the grievances:
0. It doesn’t do what you expect
Let’s look at this again:
const array1 = [1, 2, 3, 4];const reducer = (accumulator, currentValue) => accumulator + currentValue;
// expected: 1 + 2 + 3 + 4 === 10
Now I’ll modify the reducer function to add 1 to each value of the array, then sum it with the previous value. Since I’m adding 1 to four array elements, I expect to get a sum of 14.
const array1 = [1, 2, 3, 4];const reducer = (accumulator, currentValue) => accumulator + currentValue + 1;
// (1+1) + (2+1) + (3+1) + (4+1) = 2+3+4+5 = 14console.log(array1.reduce(reducer));
// unexpected output: 13
Hold on… what?
Turns out the first element of the array is the initial accumulator value. So, instead of
(1+1) + (2+1) + (3+1) + (4+1) = 2+3+4+5 = 14
It is actually:
1 + (2+1) + (3+1) + (4+1) = 2+3+4+5 = 13
All those reduce examples online that use a sum function as illustration can mislead you. You can get to the right answer, of course: just add an accumulator initial value as a second argument to the reduce method:
console.log(array1.reduce(reducer, 0));// desired output: 14
1. It’s misnamed
The name reduce would lead one to believe that it reduces an array of element to a subset of those elements. That’s not really what it does. It can do the opposite of reduce, in fact:
const array1 = [1, 2, 3, 4]; // four elementsconst increaser = (accumulator, currentValue) => { accumulator.push(currentValue); accumulator.push(currentValue*currentValue); return accumulator;};
console.log(array1.reduce(increaser, []));
// output: Array [1, 1, 2, 4, 3, 9, 4, 16] (8 elements)
And it doesn’t even have to return an array:
const array1 = [1, 2, 3, 4];const transformer = (accumulator, currentValue) => { accumulator[currentValue] = currentValue*currentValue; return accumulator;};
console.log(array1.reduce(transformer, {}));// output: Object { 1: 1, 2: 4, 3: 9, 4: 16 }
2. The order of callback arguments is weird
Let’s compare the callbacks of map(), filter(), find(), and reduce():
map: (currentValue, index, array, this) => {}
filter: (currentValue, index, array, this) => {}
find: (currentValue, index, array, this) => {}
reduce: (accumulator, currentValue, index, array) => {}
Your callback will get the accumulator as the first argument, not the currentValue; also, there is no parameter that takes a this argument.
3. Error: myVar.reduce is not a function
I’ve been spoiled by lodash, which treats both arrays and objects as collections, and collections have map, find, filter, and reduce methods. Not so JavaScript reduce:
var obj1 = {my:"dog", has: "fleas"};const values = (accumulator, currentValue) => accumulator.push(currentValue);console.log(obj1.reduce(values, []));
// > Error: obj1.reduce is not a function
As Dana Carvey used to say, “Nope, not gonna do it. Wouldn’t be prudent.” (Yes, I am that old.)
You can still do something like this, using Object.values or Object.entries:
var obj1 = {my:"dog", has: "fleas"};const values = (acc, [key, value]) => {acc.push(value); return acc}
console.log(Object.entries(obj1).reduce(values, []));
// > Array ["dog", "fleas"]
4. Accumulator sometimes optional; results sometimes random
It’s easy to forget an initial accumulator value, even when you need one. Let’s take that above example, with one modification:
var obj1 = {my:"dog", has: "fleas"};const values = (acc, [key, value]) => {acc.push(value); return acc}
console.log(Object.entries(obj1).reduce(values));
// > Array ["my", "dog", "fleas"]
Did you notice the difference in the code? Hard to spot. What’s worse, you get an array of values back, except the first is invalid. Good thing I have tests someday!
5. Undefined? Huh?
Oh, you forgot to return the accumulator. Silly you. I never make that mistake:
var obj1 = {my:"dog", has: "fleas"};const dog = (acc, [key, value]) => { if (key === 'my') { acc.push(value); return acc; }}
console.log(Object.entries(obj1).reduce(dog, []));
// > undefined
You have to always return the accumulator:
var obj1 = {my:"dog", has: "fleas"};const dog = (acc, [key, value]) => { if (key === 'my') { acc.push(value); } return acc;}
console.log(Object.entries(obj1).reduce(dog, []));
So what do I recommend instead of reduce()?
I am always using Array.reduce(), and you should, too. Just know its quirks. It’s like a tiny chihuahua nipping at your heels that sometimes will get you if you’re not paying attention, but it’s mostly harmless (and far more useful than a chihuahua).
Happy reducing!
JavaScript Array.reduce() was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.