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

Answer Source

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[0] ? data[0].value : 0) +
          (data[1] ? data[1].value : 0) +
          (data[2] ? data[2].value : 0) +
          ...
          (data[50] ? data[50].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

Addition statement
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