Reduce is a good illustration of the principle of least power[1]: it's powerful, flexible, general and low-level and can technically achieve any combination of summation, map, filter, find/includes, some/any/every, etc. But reduce is misused if it's reimplementing patterns available in higher-level form.
In cases when reduce is required because (for example) JS doesn't have a sum function, it should be kept simple. `arr.reduce((acc, el) => el + acc, 0)` is acceptable if lodash _.sum() is not available.
In cases when reduce is required because the higher-level operations like map/filter aren't flexible enough, decompose the reduction operation into simpler steps and use map/filter with multiple passes, or write a traditional for..of loop.
This principle also explains why enhanced/range/of loops are preferred over counter-based `for` loops, and counter-based loops over `while`. Technically all loops can be handled by `while`, but it's seldom needed because enhanced loops handle the common case with the cleanest syntax. Reduce/while/counter-based `for` loops are antipatterns where higher-level, less powerful abstractions exists.
One could also phrase this principle in terms of entropy (in the information-theoretic sense): If I read some code and come across filter() and map(), I already have a pretty good idea of what the code is doing, without even looking at the filter criterion or the mapping function. Entropy is low. (There's only so many filter and map functions you could write for any given type or pair of types.)
Meanwhile, if I see reduce(), "anything" could happen. (Well, of course not anything but the set of possible reducers is surely much larger.) So entropy is high.
Avoid high-entropy constructs in your code. Try to keep entropy as low as possible. (For the same reason, code with a principled approach regarding side effects is a lot better than code where any function could mutate global state at any given time.)
Why is arr.filter().map() better than arr.reduce()? Doesn't arr.reduce() only loop once through the array?
For me it's about the speed of understanding what the code does. Because filter and map are very constrained in what they can do they quickly tell me a lot about the shape of the computation I'm working with. In contrast reduce is much more flexible and so I need to do a much more detailed analysis to just answer a basic question like "is the result a scalar or another collection?".
Exactly. If I need just filter/map/some etc., I use it. But if I need a combination of more than one, that's a job for reduce().
Chained filter and map don't necessarily iterate multiple times. They certainly can but depending on how things are built they very often run as a single loop with the operations chained.