0

I have an array similar to this:

var programs_array = [
{"id":3543,"category":"1","target_revenue":1845608},
{"id":2823,"category":"1","target_revenue":1627994},
{"id":1611,"category":"1","target_revenue":1450852},
{"id":1624,"category":"1","target_revenue":25473},
{"id":4626,"category":"2","target_revenue":253048},
{"id":5792,"category":"2","target_revenue":298468},
{"id":5799,"category":"2","target_revenue":256815},
{"id":5171,"category":"2","target_revenue":239090},
{"id":4064,"category":"3","target_revenue":119048},
{"id":2322,"category":"3","target_revenue":59146},
{"id":3466,"category":"3","target_revenue":29362},
{"id":3442,"category":"3","target_revenue":149860},
{"id":1254,"category":"3","target_revenue":15600},
{"id":1685,"category":"3","target_revenue":45463}
];

I want the sum of all "target_revenue" values if "category" equals 2. Currently, I'm doing this, but I'd like to ensure I'm doing this the most efficient way.

Array.prototype.sum_cat = function (prop, cat, val) {
    var total = 0
    for ( var i = 0, _len = this.length; i < _len; i++ ) {
        if(this[i][cat]==val){total += this[i][prop]}
    }
    return total
} 

console.log('total 2: '+programs_array.sum_cat('target_revenue','category',2));

Here's a fiddle: https://jsfiddle.net/26v48djp/

Bill_VA
  • 913
  • 7
  • 12
  • You can improve the efficiency by using `let` instead of `var`: https://stackoverflow.com/questions/21467642/is-there-a-performance-difference-between-let-and-var-in-javascript – Luca Kiebel Aug 09 '18 at 18:09
  • @Luca `const` is better than both – CertainPerformance Aug 09 '18 at 18:11
  • Though `const` doesn't make much sense here, all values change – Luca Kiebel Aug 09 '18 at 18:12
  • I set up a fiddle to test all your suggestions and after testing them all, nothing really produced anything faster than 1.5 to 3 ms to execute, which is great. The array in real life is about 150 "rows" and 20 "columns" in size, which I used in testing. Actually, I played with a set of 1000 rows, and made almost no difference in time of execution. So overall, I'm pleased that all these solutions produce about the same result. https://jsfiddle.net/26v48djp/22/ – Bill_VA Aug 10 '18 at 13:40

4 Answers4

1

I would use reduce, adding to the accumulator if category is 2:

const programs_array=[{"id":3543,"category":"1","target_revenue":1845608},{"id":2823,"category":"1","target_revenue":1627994},{"id":1611,"category":"1","target_revenue":1450852},{"id":1624,"category":"1","target_revenue":25473},{"id":4626,"category":"2","target_revenue":253048},{"id":5792,"category":"2","target_revenue":298468},{"id":5799,"category":"2","target_revenue":256815},{"id":5171,"category":"2","target_revenue":239090},{"id":4064,"category":"3","target_revenue":119048},{"id":2322,"category":"3","target_revenue":59146},{"id":3466,"category":"3","target_revenue":29362},{"id":3442,"category":"3","target_revenue":149860},{"id":1254,"category":"3","target_revenue":15600},{"id":1685,"category":"3","target_revenue":45463}]

const getSum = (findCat) => programs_array.reduce((a, { category, target_revenue }) => (
  category === findCat
  ? a + target_revenue
  : a
), 0);
console.log(getSum("2"));
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
1

You could chain filter with a reduce to accomplish this easily and concisely.

const sum = programs_array.filter(e => e.category === '2').reduce((acc, element) => acc + element.target_revenue);

Or if you wanted a slightly more performant, but less concise way you could do the following. But the difference for an array of this size is likely negligible.

const sum = programs_array.reduce((acc, element) => {
    return element.category === '2' ? (acc + element.target_revenue) : acc;
});
Adam
  • 1,724
  • 13
  • 16
  • That requires constructing a *new* array and iterating over an array twice, not just once – CertainPerformance Aug 09 '18 at 18:15
  • You're correct. But for an array of this size it's not likely to have a noticable performance impact and the first solution is easier to read. I added a more performant method below that doesn't create an additional array and only iterates the source array once. But in my opinion readability is more important than performance in these situations where the gain is barely noticable. – Adam Aug 09 '18 at 18:21
0

You can simply achieve this using array.reduce()

let arr = [{"id":3543,"category":"1","target_revenue":1845608}, {"id":2823,"category":"1","target_revenue":1627994}, {"id":1611,"category":"1","target_revenue":1450852}, {"id":1624,"category":"1","target_revenue":25473}, {"id":4626,"category":"2","target_revenue":253048}, {"id":5792,"category":"2","target_revenue":298468}, {"id":5799,"category":"2","target_revenue":256815}, {"id":5171,"category":"2","target_revenue":239090}, {"id":4064,"category":"3","target_revenue":119048}, {"id":2322,"category":"3","target_revenue":59146}, {"id":3466,"category":"3","target_revenue":29362}, {"id":3442,"category":"3","target_revenue":149860}, {"id":1254,"category":"3","target_revenue":15600}, {"id":1685,"category":"3","target_revenue":45463} ];

function getSum(prop, val){
  return arr.reduce((a,curr)=> curr.category === val ? a + curr[prop] : a,0);
}
console.log(getSum("target_revenue", "2"));
amrender singh
  • 7,949
  • 3
  • 22
  • 28
0

In general, a for loop is going to be the most efficient solution.

See: https://hackernoon.com/javascript-performance-test-for-vs-for-each-vs-map-reduce-filter-find-32c1113f19d7

I would use your code with slight modifications as seen below:

Array.prototype.sum_cat = function (prop, cat, val) {
    let total = 0,
        _len = this.length; 
    for (let i = 0; i < _len; i++) {
        if (this[i][cat] == val) { total += this[i][prop]; }
    }
    return total;
} 

console.log('total 2: '+programs_array.sum_cat('target_revenue','category',2));
S. Walker
  • 2,129
  • 12
  • 30