DEV Community

Discussion on: Is reduce() bad?

Collapse
 
vonheikemen profile image
Heiker

reduce is not bad, it's just unfamiliar. In the javascript community for loops are so common and taught so early on that is often seen as simple and easy. People like easy so they will fight you if you try to take that away from them.

Collapse
 
lucianohgo profile image
Luciano H. Gomes

If it offers little to no value, as .reduce does, then the easier/simpler way has to be considered the best.

Collapse
 
vonheikemen profile image
Heiker

Why do you think .reduce has little to no value?

.reduce follows a pattern and has a use case, just like .map and .filter.

Thread Thread
 
lucianohgo profile image
Luciano H. Gomes

Almost any use case reduce has is best served by a .forEach or through .map or .filter. Most of the time it's useless complexity

Thread Thread
 
simme profile image
Simme

It really isn’t a good comparison though, as reduce has a different use case. For instance, reducing an array into a single value would not be possible with map nor filter (hence the name reduce).

It is possible to do what alll three of them do with forEach, if you create a variable holding the result outside of the callback function, but personally I think that looks worse and disconnects the result from the operation.

Thread Thread
 
lucianohgo profile image
Luciano H. Gomes • Edited

Since "reducing to a value" is rather ample, i.e. the value could be an array, an object, or whatever some use cases can be supplemented by .map() and .filter().

Regarding the forEach, yep that's true, all of them could be replaced, but reduce is the only one that actually adds complexity and I'd argue it's the one that adds the least value. .map() and .filter() reduce complexity from reading the code, .reduce() adds to it and that's why I don't favor it much. At least, besides simply summations, multiplications, etc I haven't really seen a case where I'd feel it was a good use yet.

Thread Thread
 
simme profile image
Simme

In what way do .reduce() add complexity while .map() and .filter() reduce it? It would be awesome if you'd care to expand on this. 😅

Thread Thread
 
lucianohgo profile image
Luciano H. Gomes

Ofc, .map() and .filter() are very clear on intention and have a simpler API to grasp. e.g.:

Code showing how .map doesn't complicate the API
*Taken from *

While .reduce() is less readable (unless when used for things that are simple like simple arithmetical operations). e.g.

Code showing how to log in order with and without reduce
Taken from: developers.google

Of course, those are examples, and we can find ones in which .reduce() wouldn't harm readability so much as the one above. But for most use cases I've seen in the wild, it does. It simply is harder to read and reason about to a lot of people. That's why even though I can understand it, I steer clear from using it. In general, Code will be read a lot more times than it will be written so even when we have to add one or two extra lines, they are usually easily paid for ten-fold by gains in readability :)

Thread Thread
 
vonheikemen profile image
Heiker

Ofc, .map() and .filter() are very clear on intention

Funny that you say that because .reduce is a really good name. It can be used for many things but the ideal use case it's in the name.

and have a simpler API to grasp.

Sure, both .map and .filter are easier to explain because they focus on individual items and nothing more. Now .reduce is another story, it can transform the entire array into something else so you can say that it has a bigger scope.

About those snippets.

Of course, those are examples, and we can find ones in which .reduce() wouldn't harm readability so much as the one above. But for most use cases I've seen in the wild, it does. It simply is harder to read and reason about to a lot of people.

Showing complex (sometimes bad) examples and putting the blame on .reduce doesn't do anyone a favor. It makes harder for people to see what is good for.

While .reduce() is less readable (unless when used for things that are simple like simple arithmetical operations)

That's what I'm talking about, it doesn't look like you know when it's safe to use. Arithmetical operations are just an example, the pattern that you should be looking for is a function that combines two values of the same type. In the second snippet on logInOrder, that is exactly what happens. We can improve the readability on that snippet if we recognize that pattern.

function logInOrder(urls) {
  // fetch all the URLs in parallel
  const textPromises = urls.map(url => {
    return fetch(url).then(resp => resp.text());
  });

  // Combine two promises and log the result of the second
  const chainPromises = (firstTask, secondTask) => {
    return firstTask.then(() => secondTask).then(console.log);
  };

  // log them in sequence
  return textPromises.reduce(chainPromises, Promise.resolve());
}

The first thing we do is stop trying to do everything in one expression. The other thing you might notice is that the callback chainPromises doesn't treat the data like a special snowflake inside a .reduce, it's a generic function that takes two promises and returns a new one. When you add .reduce into the mix it should become clear we are reducing an array of promises into a single promise. That said, I still think this is a bad use case for .reduce because the goal of the function is to produce a side effect, that alone should exclude it as the right tool in this case.

I want to say more but I think this is enough for now. If anyone wants to know more, I wrote about it here: Reduce: how and when.

Collapse
 
jasterix profile image
Jasterix

That was my honestly biggest challenge with learning reduce(). My brain was too used to the syntax of for loops. But it is also difficult to understand a reduce method that's doing 3+ things in one function

Collapse
 
vonheikemen profile image
Heiker

But it is also difficult to understand a reduce method that's doing 3+ things in one function

I get it. It happens. But do you think is easier to read a for block that is doing 3+ things?

Thread Thread
 
jasterix profile image
Jasterix

For me, the simple fact that I learned for loops first has meant that my brain defaults to it. I'm more fluent with for loops. So any other syntax means having to "translate".

Collapse
 
kenbellows profile image
Ken Bellows

Gotta disagree here. In my experience, especially over the last several years, array methods have been pushed super hard in the JS community. I rarely see a simple for loop (of any flavor) these days; it's always long chains of .map().filter().map().reduce().flatMap()... Simple for loops are often criticized with a "why didn't you just use Array.{whatever}?".

Not that this is bad on the whole, I often find this much easier to read than a bunch of for loops. But that's the point that Jake makes in the video referenced in this article, and I find his examples pretty convincing: among all the array methods, reduce is by far the least readable, and it often requires time and mental gymnastics to figure out what a reduce function is doing, where a simple for loop would have been trivial to follow.

Collapse
 
vonheikemen profile image
Heiker • Edited

Gotta disagree here. In my experience, especially over the last several years, array methods have been pushed super hard in the JS community.

Sure the amount of content about map, filter and reduce has increased over the years but that is something you learn later down the road. I would imagine that for and while loops are still the prefered way to teach beginners about iteration.

I rarely see a simple for loop (of any flavor) these days; it's always long chains of .map().filter().map().reduce().flatMap()...

To be fair map is actually quite useful.

that's the point that Jake makes in the video referenced in this article, and I find his examples pretty convincing: among all the array methods, reduce is by far the least readable, and it often requires time and mental gymnastics to figure out what a reduce function is doing,

I like that they show cases where reduce is completely unnecesary, but that doesn't make reduce bad it's just easier to misuse. In some cases the thing that hurt readability is the inline callback with clever code trying to do everything in one expression.

Collapse
 
stackundertow profile image
StackUndertow

Late to the party, but I wanted to chime in.

Maybe I'm a gymnast, but reduce functions don't really seem all that hard to grok since they always do the same thing mechanically:

reduce((seed, next) => {
// evaluate next
// update seed or data type of seed
// return seed or data type of seed
});

Given that, any readability argument comes down to familiarity. If you've never seen a for loop (some languages lack them, ex: Haskell), you may wonder what arcane nonsense for(initializer, evaluator, incrementor){ //operation } is and why the initializer value is available in the context of the operation since the primary tool in your belt is recursion.

I would argue that the worst thing to do is mix and match. What becomes unintuitive is jumping in and out of imperative and FP concepts and having to deal with the jarring discontinuity in paradigm.

Now if I had to pick and argument for not using JS array methods at all, it would be they're not lazy and memory abusive. But that's a completely different subject.