Once people are familiar with reduce, it looks just like any other boiler-plate. The callback function used is the "reducer". I would find it weird if I saw React people preferring the old for-loop.
I think the test-ability argument is a moot point in the thread, because if there are many conditions and a really fat reducer function, that needs testing, not the reducer in the context of a reduce invocation.
An argument can be made on both grounds. Functional AND declarative code is typically preferred. A lot of people have already used this example but in a less readable fashion, but let's really illustrate declarative code:
// simple to unit test this reducerfunctionmaximum(max,num){returnMath.max(max,num);}// read as: 'reduce to a maximum' letnumbers=[5,10,7,-1,2,-8,-12];letmax=numbers.reduce(maximum);
I like this approach (plus I finally understood where reducer comes from :D - in .NET it's called Aggregate). However, I also think that if one is not going to (re)use multiple types of reducers, there is no need to use reduce to emulate a forEach or even a normal loop.
But congratulations on the conciseness of your code!
Yeah, it was a contrived example from many people in this thread. I also don't use JS regularly at all anymore which is why I often default to ES5 syntax.
I typically end up using reduce for "partitioning" or performing only a single iteration through the collection to include/exclude/filter into a different data type, like a set, map, etc to keep things atomic... we've all seen people declare combination of multiple arrays, array and object, array and map, etc at the TOP of the file and the actual for-loop is modifying it 100 lines down, and some other code that modifies it in between... that's really really really bad and shitty for anyone needing to debug it. These functional facilities over collections make them atomic and you know where to look if there's a bug -- the reducer (hopefully unit-tested)...
I've used reduce for a lot of this (changing data-types, and accumulation to a different collection type)
letnumStrings=['1','2','3'];numStrings.reduce((map,num)=>{constnum=parseInt(num);returnnum%2===0?map.evens.push(num):map.odds.push(num);},{evens:[],odds:[]});// RESULT (strings are converted to integers in a nice map/object of evens and odds)// {evens: [2], odds: [1, 3]}
Some people call this "partitioning". The above would be difficult to express with just a filter call or for-loop without scattering variables. The thing that gets most people is where the "initial value" {evens: [], odds: []} is. People are just very used to seeing it right above an iteration
I see what you are saying, but unless you are going to reduce that function, this can just as well be expressed by a forEach or a loop. One declaration of a variable above it doesn't bother me. Cheers!
forEach just abstracts away the iterator variable boilerplate, but could still leave the collection variables scattered about, like at the top of the file and make it possible for someone to mutate them in between... but yah!
Pardon for jumping in but fold are very well studied functions, as you know :) Sorry for dropping a link, but it speaks much better than I could: Fold Higher Order Function
@wulymammoth
your code is slightly wrong. You have to return the accumulator (the object of arrays, this case). You're instead returning whatever push returns (which is the length of the array after pushing). (Also, you're re-declaring the parameter num as a const, which isn't allowed, but that's a simpler fix.)
This about sums it up.
Once people are familiar with reduce, it looks just like any other boiler-plate. The callback function used is the "reducer". I would find it weird if I saw React people preferring the old for-loop.
I think the test-ability argument is a moot point in the thread, because if there are many conditions and a really fat reducer function, that needs testing, not the reducer in the context of a reduce invocation.
An argument can be made on both grounds. Functional AND declarative code is typically preferred. A lot of people have already used this example but in a less readable fashion, but let's really illustrate declarative code:
I like this approach (plus I finally understood where reducer comes from :D - in .NET it's called Aggregate). However, I also think that if one is not going to (re)use multiple types of reducers, there is no need to use reduce to emulate a forEach or even a normal loop.
But congratulations on the conciseness of your code!
Yeap! I've never done C#, but the analogs are listed in this SO thread: stackoverflow.com/questions/428798...
Yeah, it was a contrived example from many people in this thread. I also don't use JS regularly at all anymore which is why I often default to ES5 syntax.
I typically end up using reduce for "partitioning" or performing only a single iteration through the collection to include/exclude/filter into a different data type, like a set, map, etc to keep things atomic... we've all seen people declare combination of multiple arrays, array and object, array and map, etc at the TOP of the file and the actual for-loop is modifying it 100 lines down, and some other code that modifies it in between... that's really really really bad and shitty for anyone needing to debug it. These functional facilities over collections make them atomic and you know where to look if there's a bug -- the reducer (hopefully unit-tested)...
I've used reduce for a lot of this (changing data-types, and accumulation to a different collection type)
Some people call this "partitioning". The above would be difficult to express with just a filter call or for-loop without scattering variables. The thing that gets most people is where the "initial value"
{evens: [], odds: []}
is. People are just very used to seeing it right above an iterationI see what you are saying, but unless you are going to reduce that function, this can just as well be expressed by a forEach or a loop. One declaration of a variable above it doesn't bother me. Cheers!
forEach
just abstracts away the iterator variable boilerplate, but could still leave the collection variables scattered about, like at the top of the file and make it possible for someone to mutate them in between... but yah!Pardon for jumping in but fold are very well studied functions, as you know :) Sorry for dropping a link, but it speaks much better than I could: Fold Higher Order Function
@wulymammoth your code is slightly wrong. You have to return the accumulator (the object of arrays, this case). You're instead returning whatever push returns (which is the length of the array after pushing). (Also, you're re-declaring the parameter
num
as a const, which isn't allowed, but that's a simpler fix.)All good catches
Yeap! Just re-using the maximum example that others have used for illustration purposes only. JS already has this available in the standard library :)
Calculating
lcm
of more than two numbers also becomes impressively simple and beautiful. For example my AoC day 12 solution.☝️