Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any real benefit for using javascript Array reduce() method?

Tags:

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

like image 624
Evan You Avatar asked Mar 09 '12 05:03

Evan You


People also ask

Should you use reduce in JavaScript?

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.

What is the purpose of the reduce method on an array?

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.

Is array reduce efficient?

And testing on JSPerf shows that reduce() is usually 60%-75% slower, depending on the operations performed inside each iteration.

What is use of array reduce in JavaScript?

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.


2 Answers

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 
like image 164
Pavlo Bashynskyi Avatar answered Nov 09 '22 10:11

Pavlo Bashynskyi


  • You might want scoping. For example you might want to make callback functions or have references to javascript objects. For more information, see why javascript is not blocked scoped. [edit: modern javascript now supports 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.
  • Your code does not always need to run at full machine speed. You may not even be optimizing code in the bottleneck.
  • Additionally you do not provide your "testing on JSPerf" so we can critique it. For example if you already have a reduction function (or map or forEach function), then I bet the performance would be on-par. Even if not, the testing methodology may be flawed, especially given that many browsers may optimize differently or have different function-call overhead.

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.)

like image 23
ninjagecko Avatar answered Nov 09 '22 09:11

ninjagecko