Most use cases of the reduce() method can be easily rewritten with a for loop. And testing on JSPerf shows that reduce() is usually 60%-75% slower, depending on the operations performed inside each iteration.
Is there any real reason to use reduce() then, other than being able to write code in a 'functional style'? If you can have a 60% performance gain by writing just a little bit more code, why would you ever use reduce()?
EDIT: In fact, other functional methods like forEach() and map() all show similar performance, being at least 60% slower than simple for loops.
Here's a link to the JSPerf test (with function calls): forloop vs forEach
The easiest and perhaps most popular use of the JavaScript reduce method is to get the sum total of a list of numbers. On every iteration, we simply added the current teams' points to the previously accumulated points, and then we returned that as the accumulated value for the next iteration.
The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.
And testing on JSPerf shows that reduce() is usually 60%-75% slower, depending on the operations performed inside each iteration.
JavaScript Array reduce() The reduce() method executes a reducer function for array element. The reduce() method returns a single value: the function's accumulated result. The reduce() method does not execute the function for empty array elements. The reduce() method does not change the original array.
The performance of the methods may vary depending on the size of the data. Speed is also affected by compiler optimization and data warm-up. Therefore on small data for of
wins, and on big reduce
insignificantly wins.
You can see for yourself by running the test:
const LOOP = 3 test(dataGenerator(5)) test(dataGenerator(500)) test(dataGenerator(50000)) test(dataGenerator(500000)) test(dataGenerator(5000000)) function test(dataSet) { let sum console.log('Data length:', dataSet.length) for (let x = 0; x < LOOP; x++) { sum = 0 console.time(`${x} reduce`) sum = dataSet.reduce((s, d) => s += d.data, 0) console.timeEnd(`${x} reduce`) } for (let x = 0; x < LOOP; x++) { sum = 0 console.time(`${x} map`) dataSet.map((i) => sum += i.data) console.timeEnd(`${x} map`) } for (let x = 0; x < LOOP; x++) { sum = 0 console.time(`${x} for loop`) for (let i = 0; i < dataSet.length; i++) { sum += dataSet[i].data } console.timeEnd(`${x} for loop`) } for (let x = 0; x < LOOP; x++) { sum = 0 console.time(`${x} for reverse`) for (let i = dataSet.length; i--;) { sum += dataSet[i].data } console.timeEnd(`${x} for reverse`) } for (let x = 0; x < LOOP; x++) { sum = 0 console.time(`${x} for of`) for (const item of dataSet) { sum += item.data } console.timeEnd(`${x} for of`) } for (let x = 0; x < LOOP; x++) { sum = 0 console.time(`${x} for each`) dataSet.forEach(element => { sum += element.data }) console.timeEnd(`${x} for each`) } console.log() } function dataGenerator(rows) { const dataSet = [] for (let i = 0; i < rows; i++) { dataSet.push({id: i, data: Math.floor(100 * Math.random())}) } return dataSet }
These are the results of a performance test on my laptop. for loop
does not work stably unlike for reverse
and for of
.
➜ node reduce_vs_for.js Data length: 5 0 reduce: 0.127ms 1 reduce: 0.008ms 2 reduce: 0.006ms 0 map: 0.036ms 1 map: 0.007ms 2 map: 0.018ms 0 for loop: 0.005ms 1 for loop: 0.014ms 2 for loop: 0.004ms 0 for reverse: 0.009ms 1 for reverse: 0.005ms 2 for reverse: 0.004ms 0 for of: 0.008ms 1 for of: 0.004ms 2 for of: 0.004ms 0 for each: 0.046ms 1 for each: 0.003ms 2 for each: 0.003ms Data length: 500 0 reduce: 0.031ms 1 reduce: 0.027ms 2 reduce: 0.026ms 0 map: 0.039ms 1 map: 0.036ms 2 map: 0.033ms 0 for loop: 0.029ms 1 for loop: 0.028ms 2 for loop: 0.028ms 0 for reverse: 0.027ms 1 for reverse: 0.026ms 2 for reverse: 0.026ms 0 for of: 0.051ms 1 for of: 0.063ms 2 for of: 0.051ms 0 for each: 0.030ms 1 for each: 0.030ms 2 for each: 0.027ms Data length: 50000 0 reduce: 1.986ms 1 reduce: 1.017ms 2 reduce: 1.017ms 0 map: 2.142ms 1 map: 1.352ms 2 map: 1.310ms 0 for loop: 2.407ms 1 for loop: 12.170ms 2 for loop: 0.246ms 0 for reverse: 0.226ms 1 for reverse: 0.225ms 2 for reverse: 0.223ms 0 for of: 0.217ms 1 for of: 0.213ms 2 for of: 0.215ms 0 for each: 0.391ms 1 for each: 0.409ms 2 for each: 1.020ms Data length: 500000 0 reduce: 1.920ms 1 reduce: 1.837ms 2 reduce: 1.860ms 0 map: 13.140ms 1 map: 12.762ms 2 map: 14.584ms 0 for loop: 15.325ms 1 for loop: 2.295ms 2 for loop: 2.014ms 0 for reverse: 2.163ms 1 for reverse: 2.138ms 2 for reverse: 2.182ms 0 for of: 1.990ms 1 for of: 2.009ms 2 for of: 2.108ms 0 for each: 2.226ms 1 for each: 2.583ms 2 for each: 2.238ms Data length: 5000000 0 reduce: 18.763ms 1 reduce: 17.155ms 2 reduce: 26.592ms 0 map: 145.415ms 1 map: 135.946ms 2 map: 144.325ms 0 for loop: 29.273ms 1 for loop: 28.365ms 2 for loop: 21.131ms 0 for reverse: 21.301ms 1 for reverse: 27.779ms 2 for reverse: 29.077ms 0 for of: 19.094ms 1 for of: 19.338ms 2 for of: 26.567ms 0 for each: 22.456ms 1 for each: 26.224ms 2 for each: 20.769ms
let
variables. Back before ESv6, when you declared a var
variable, it got hoisted as if it was written at the top of the function codeblock, so often you had to write bodies of for-loops as functions. This following still applies though:] If you had a function written, might as well use functional style unless it's a significant bottleneck.sidenote: this is a valid performance comparison between syntax, but an invalid performance comparison in when syntax is not the question at hand:
myArray.map(function(x){return x+1}) // ...versus... for(var i=0; i<myArray.length; i++) { myArray[i] = myArray[i]+1; }
This would be a valid performance comparison:
myArray.forEach(function(x){return x+1}) // ...versus... var plusOne = function(x){return x+1}; for(var i=0; i<myArray.length; i++) { plusOne(myArray[i]); } // (may need a side-effect if the compiler is smart enough to optimize this)
(Also in reply to your edit: .forEach()
and .map()
provide much more clarity, and avoid the need for explicit loop int i=0; i<array.length; i++
arguments.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With