I was reading another dev.to post, Demystifying Array.reduce(), but I didn't feel convinced about using Array.reduce().
Maybe I too am not using Array.reduce() the right way, but every time I do, I end up disliking it and switching to a simple for..of loop.
Up ahead are the three examples from that article, converted to use for..of and in my opinion easier to read and cleaner.
Take for example the sum example:
const array = [1, 2, 3, 4];
const sum = array.reduce((accumulator, currentItem) => {
return accumulator + currentItem;
}, 0);
// sum = 10
It can be written as
const array = [1, 2, 3, 4]
let sum = 0
for (const n of array) sum += n
// sum = 10
That's simpler!
The next example,
const trips = [{type: 'car', dist: 42}, {type: 'foot', dist: 3}, {type:'flight', dist: 212}, {type: 'car', dist: 90}, {type: 'foot', dist: 7}]
const distanceByType = trip.reduce((out, curr) => {
const { type, dist } = curr;
if (out[type]) {
out[type] += dist;
} else {
out[type] = dist;
}
return out;
}, {});
// distanceByType = {car: 132, foot: 10, flight: 212};
can be rewritten as
const trips = [{type: 'car', dist: 42}, {type: 'foot', dist: 3}, {type:'flight', dist: 212}, {type: 'car', dist: 90}, {type: 'foot', dist: 7}]
const distanceByType = {}
for (const trip of trips) {
const { type, dist } = trip
if (distanceByType[type]) {
distanceByType[type] += dist
} else {
distanceByType[type] = dist
}
}
// distanceByType = {car: 132, foot: 10, flight: 212}
Simple!
Finally, the example from the comments about piping functions,
const pipeOnce = (fn1, fn2) => (args) => (fn2(fn1(args)));
const pipe = (...ops) => ops.reduce(pipeOnce);
const addTwo = a => a + 2;
const mulTwo = a => a * 2;
const addTwoMulTwo = pipe(addTwo, mulTwo);
console.log(addTwoMulTwo(1)); // (1 + 2) * 2 => 6
console.log(addTwoMulTwo(2)); // (2 + 2) * 2 => 8
console.log(addTwoMulTwo(3)); // (3 + 2) * 2 => 10
is a better of example of reduce, but it can be written as
const addTwo = a => a + 2;
const mulTwo = a => a * 2;
const addTwoMulTwo = n => mulTwo(addTwo(n))
console.log(addTwoMulTwo(1)); // (1 + 2) * 2 => 6
console.log(addTwoMulTwo(2)); // (2 + 2) * 2 => 8
console.log(addTwoMulTwo(3)); // (3 + 2) * 2 => 10
If we want to pipe an arbitrary number of functions, we can do it with for..of too:
const pipe = (...fns) => arg => {
for (const fn of fns) arg = fn(arg)
return arg
}
const addTwoMulTwo = pipe(addTwo, mulTwo)
This one isn't as short, but it is easier to understand.
What are some use cases where Array.reduce() really shines over alternatives like for..of?
Please share if you know!
Latest comments (30)
I'm happy because as soon as I entered the post I saw this sentence:
okay that was very fast xD i love it
for ofinstead of ugly, unintuitive , larga, heavy, and don't funnyreducemethod. match forever, sorry reduce i hope never see you again.i found as a cons:
you can't chain methods... or interrupt a possible chain of methods with a
for ofas a pros:
legibilityperformanceagain prefer
for ofof courseI haven't seen any mention of forEach in this thread. So I wonder why do we still need reduce when forEach can achieve exactly the same result and number of code lines?
Totally! And that is also much more readable!
I use
Array.prototype.reducea lot. The reason why is because v8 and firefox does a very good job of optimizing reduce calls when it gets optimized.Often in the case of clarity over speed, using
for..ofloops is the best. Sometimes, it's not always about clarity.Good point. Thanks for that perspective!
Your first example can be solved like this:
Your second example can be solved like this:
Both example look easier with reduce, I think
You are creating one object per iteration! This can't perform well with large arrays...
Those are shorter, but at a glance it still takes me more time to understand it than the for..of loops. Maybe it's just how my mind is trained.
It also helps a lot when one has a phobia of statements.
Ah, but now the sum is mutable! Who knows what will happen to it!
Seriously, man? for..of?
Sometimes it's better to use for..of (e.g. when you need some boost iterating over a huge collection you can do all the stuff in one loop) but functional style is much more useful when you are trying to express yourself to your colleagues.
My head nearly exploded looking at that. Map reduce is not a good pattern for application engineering, it's a backend big-unstructured-data pattern.
retrievePostsFor(me)would be so much easier to think about as well. Baking in getting all posts and filtering is just not clear unless you have a shallow micro-service code-base, embrace the cascade.Finally wrap that up in a method
to anyone liking this, it works better if you just send in users. Then it doesn't matter where they come from, and anything with 0 likes just adds items to the list...
In-fact it doesn't need to be a user at all. Just something implementing a likeable interface, which has a method to retrieve likes.
Is this correct?
It seems for me pipeOnce should be
Though it would not be 'pipeOnce' by semantic anymore:)
Heh, well this shows that thinking about it is more complicated than the for..of loops.
Mutation transfers a var X from state A to state B, so now, when we intend to use X we need to check is it A or B.
We actually do mutations for 'complex behaviour' - ability to launch different execution branches for the same input signals, which to launch is determined by current state of X. In such case X would be 'control state' (FSM term).
It is the appliance where it's truly needed, others are adding accidental complexity to simple data/value transformation.
Thanks for the post, got me thinking
First example could be simpler with reduce, I mean, if you use n as variable in the for version, why not using n in the reduce one?
const array = [1, 2, 3, 4];
const sum = array.reduce((a, n) => a + n, 0);
Actually, this is better as you can extract the reducer part
(a, n) => a + nas a function. The other way is not reusable.