Array of Properties -> Sum

This is more of a… “is there a” (read: What is the) “more efficient way of doing this” question.

I have a restricted environment that I do not control. Pure Javascript only.

  • I have an array of associative indexes as strings. ['astring','bstring','cstring',....]
  • I have an object that may or may not contain those indexes as properties, along with other properties not contained in the indexes. { "astring": "12", "cstring":"20","wark": "Hrmbubble."}
  • The value of an indexed property in the object will contain an integer string.

Is there a better way to sum the value of the selected indexes, short of foreaching the array?

Yes there is, you can filter for the appropriate fields from the object, and pass them through a sum operation.

var propsToSum = ['astring','bstring','cstring'];
var obj =  {
    "astring": "12",
    "cstring":"20",
    "wark": "Hrmbubble."
};
var sum = propsToSum
    .map(prop => obj[prop])
    .filter(hasValue => hasValue)
    .map(value => Number(value))
    .reduce((a, b) => a + b);
console.log(sum); // 32

The map gets the appropriate values from the object.
The filter removes any undefined values from when the property wasn’t found.
The second map turns the strings into a number.
And the reduce adds the numbers together.

2 Likes

Very nice, Paul!

For reference, my original code was (values is the object, ranks is the array):

    var totalRank = 0;
	ranks.forEach(x=> totalRank += (values.hasOwnProperty(x)) ? parseInt(values[x]) : 0);

That code is a overcomplicated misuse of array methods. Why would you use 4 loops to calculate the sum?

var sum = propsToSum .map(prop => obj[prop]) .filter(hasValue => hasValue) .map(value => Number(value)) .reduce((a, b) => a + b); console.log(sum); // 32

is like doing [code]for(var i=0, sum=0; i<propsToSum.length; i++)
propsToSum[i] = obj[propsToSum[i]];
for( i=0; i<propsToSum.length; i++)
if(!propsToSum[i])
propsToSum.splice(i, 1);
for( i=0; i<propsToSum.length; i++)
propsToSum[i] = Number(propsToSum[i]);
for(i=0; i<propsToSum.length; i++)
sum += propsToSum[i];

console.log(sum);[/code]

where you could suffice using only array the reduce method.

var sum = propsToSum.reduce((total, prop) => total + values.hasOwnProperty(x) ? parseInt(values[x]) : 0, 0); console.log(sum);

[quote=“alperquin, post:4, topic:293896, full:true”]
That code is a overcomplicated misuse of array methods. Why would you use 4 loops to calculate the sum?[/quote]

Because raw calculation performance is of a lower priority than pipelining, which gives clear intent in the code, making it easier to understand and modify.

3 Likes

I tried using your condensed code and found that there are some bugs in it.

x doesn’t refer to anything, so I presume that you mean prop?

var propsToSum = ['astring','bstring','cstring'];
var obj =  {
    "astring": "12",
    "cstring":"20",
    "wark": "Hrmbubble."
};
var sum = propsToSum.reduce((total, prop) => total + values.hasOwnProperty(prop) ? parseInt(values[prop]) : 0, 0);
console.log(sum);

And values isn’t defined, so I’ll rename obj to values

var propsToSum = ['astring','bstring','cstring'];
// var obj =  {
var values =  {
    "astring": "12",
    "cstring":"20",
    "wark": "Hrmbubble."
};
var sum = propsToSum.reduce((total, prop) => total + values.hasOwnProperty(prop) ? parseInt(values[prop]) : 0, 0);
console.log(sum);

And the resulting sum is 0. What’s going wrong with your code?

Meanwhile with my code I found when testing with an array of 1000 that there was a bug, which has been easily fixed by moving filter down below map.

Before:

var sum = propsToSum
    .map(prop => obj[prop])
    .filter(hasValue => hasValue)
    .map(value => Number(value))
    .reduce((a, b) => a + b);

After:

var sum = propsToSum
    .map(prop => obj[prop])
    .map(value => Number(value))
    .filter(hasValue => hasValue)
    .reduce((a, b) => a + b);
1 Like

FWIW, I can guarantee that values will never contain more than 100 properties. But the abstraction to large objects is appreciated.

1 Like

If we can get @alperquin to put together a working version of his code, I’ll get some performance tests done to see how the two styles compare.

My mistake, I posted the wrong variant. This version should work.var sum = propsToSum.reduce((total, prop) => total + (Number(obj[prop]) || 0), 0); console.log(sum)

[quote=“alperquin, post:9, topic:293896, full:true”]
My mistake, I posted the wrong variant.[/quote]

Thanks, according to my tests using jsperf the functional code is only 27% slower.
Separate tests using the console timer reports that both sets of code take 0ms to run against 100 entries.

Performance is not a factor here, or in any reasonable use-case.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.