 Sumurai8 - 4 years ago 216
Javascript Question

# Deferred assignment in pure javascript

In this question I encountered the following simplified problem:

We start with an array of Objects with a value attribute. We want to calculate for each value what percentage of the sum of values it is, and add it to the structure as a property. To do this, we need to know the sum of values, but this sum is not calculated beforehand.

``````//Original data structure
[
{ "value" : 123456 },
{ "value" : 12146  }
]

//Becomes
[
{
"value" : 123456,
"perc"  : 0.9104
},
{
"value" : 12146 ,
"perc"  : 0.0896
}
]
``````

An easy, and probably most readable, solution is to go through the data structure twice. First we calculate the sum, then we calculate the percentage and add it to the data structure.

``````var i;
var sum = 0;
for( i = 0; i < data.length; i++ ) {
sum += data[i].value;
}
for( i = 0; i < data.length; i++ ) {
data[i].perc = data[i].value / sum;
}
``````

Can we instead just go through the data structure once, and somehow tell that the percentage expression should only be evaluated once the entire sum is known?

I am primarily interested in answers that address pure javascript. That is: Without any libraries. Anonymous

The only way to make this with one less loop is to write out the whole sum statement made up of all possible items, for instance

``````var sum = (data ? data.value : 0) +
(data ? data.value : 0) +
(data ? data.value : 0) +
...
(data ? data.value : 0);

for( i = 0; i < data.length; i++ ) {
data[i].perc = data[i].value / sum;
}
``````

You could use Array's reduce function but that is still a loop in the background, and a function call for each array element:

``````var sum = data.reduce(function(output,item){
return output+item.value;
},0);
for( i = 0; i < data.length; i++ ) {
data[i].perc = data[i].value / sum;
}
``````

You could use the ES6 Promise, but there you are still adding a bunch of function calls

``````var data = [
{ "value" : 123456 },
{ "value" : 12146  }
]
var sum = 0;
var rej = null;
var res = null;
var def = new Promise(function(resolve,reject){
rej = reject;
res = resolve;
});
function perc(total){
this.perc = this.value/total;
}

for( i = 0; i < data.length; i++ ) {
def.then(perc.bind(data[i]));
sum+=data[i].value;
}
res(sum);
``````

Perf Tests

10,834,196
±0.44%
fastest

Reduce
3,552,539
±1.95%
67% slower

Promise
26,325
±8.14%
100% slower

For loops
9,640,800
±0.45%
11% slower

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download