2

I have an array like this:

var data = 
    [
       {label:1, quater :'Q1', y:34},
       {label:2, quater:'Q1', y:20},
       {label:3, quater:'Q1', y:30},

       {label:1, quater:'Q2', y:77},
       {label:2, quater:'Q2', y:52},
       {label:3, quater:'Q2', y:3},

       {label:1, quater:'Q3', y:65},
       {label:2, quater:'Q3', y:12},
       {label:3, quater:'Q3', y:9},

       {label:1, quater:'Q4', y:77},
       {label:2, quater:'Q4', y:34},
       {label:3, quater:'Q4', y:5}
    ];

My goal is to add "percent" into each object. Each "percent" is the percentage of the value of y in current object devided by the sum of values of y in quater from Q1 to Q4. For example, the percent in first boject should be 34/(34+77+65+77) The code below is what I tried. It works and calculats the data correctly. I am new to javascript, so I calculate the data step by step which makes the code look long.

I am wondering what is the smarter way to calculate the data to make the code shorter? Thanks!

var quater = ["Q1","Q2","Q3","Q4"];
var sum = 0;
var arr = [];
for(var i = 0; i<quater.length; i++){
  for(var j = 0; j<data.length; j++){
    if (data[j]['quater'] == quater[i])
      sum = sum + data[j]['y'];        
  }
 //console.log(sum)    
 arr.push({
   'quater': quater[i],
   'sum': sum
 })
  sum = 0;
}
var arr1 = [];
var percent;
for(var i = 0; i<data.length;i++){
  for(var j = 0; j<arr.length; j++){
    if(data[i]['quater'] == arr[j]['quater']){
      percent = data[i]['y']/arr[j]['sum'];
    }
  }
  //console.log(percent)
  arr1.push({
    'label': data[i]['label'],
    'y': data[i]['y'],
    'quater': data[i]['quater'],
    'percent': percent
  });
}

console.log(arr1)

3 Answers 3

5

Can make a simple object that has quater's as keys and has the sums as values.

Then iterate array again and calculate percentage

var totals={};

data.forEach(function(item){
  totals[item.quater] = (totals[item.quater] || 0 ) + item.y;
});

// totals now looks something like {Q3:18, Q1:16, Q4:66, Q1:12}

data.forEach(function(item){
   item.percent = 100 * item.y / totals[item.quater];
});

Assumes label is unimportant such as representing year. If not concatenate label , delimiter and quater into object keys

EDIT: i may have this backwards and need label's as keys. Approaach is same

Sign up to request clarification or add additional context in comments.

2 Comments

got 3 mins late, had made exactly same solution but with map instead of forEach haha
This is uncanny. My code was literally identical to yours, right down to the totals and item names. The only exception was the multiplication by 100 - there is some confusion about that in the question, so I left that out with a big TODO note about it. And a bit of difference in whitespace - I like spaces around the = and inside the parentheses, and between the ) { too. Only mentioning those to show how minor the differences were - it was quite remarkable to see that we wrote the exact same code. I think that makes this the canonical solution. :-)
1

You can use forEach or map to this more efficiently. The first map is used to calculate the totals, the second map is used to calculate the percentages. See charlietfl answer, who beat me to the answer, for the forEach method which is pretty much identical.

var data = 
    [
       {label:1, quater :'Q1', y:34},
       {label:2, quater:'Q1', y:20},
       {label:3, quater:'Q1', y:30},

       {label:1, quater:'Q2', y:77},
       {label:2, quater:'Q2', y:52},
       {label:3, quater:'Q2', y:3},

       {label:1, quater:'Q3', y:65},
       {label:2, quater:'Q3', y:12},
       {label:3, quater:'Q3', y:9},

       {label:1, quater:'Q4', y:77},
       {label:2, quater:'Q4', y:34},
       {label:3, quater:'Q4', y:5}
    ];

quartersum = {}

data.map(function(entry) {
  
  quartersum[entry.label] = (quartersum[entry.label]||0) + entry.y
  
})

data.map(function(entry) {
  
  entry.percent = entry.y / quartersum[entry.label] * 100;
  
  document.write(JSON.stringify(entry) + '<br>')
})

Comments

1

Try this.

I just followed the procedure of pushing the objects simultaneoulsy as I was calculating the percent values.

<script>
    var data =
            [
                {label:1, quarter :'Q1', y:34},
                {label:2, quarter:'Q1', y:20},
                {label:3, quarter:'Q1', y:30},

                {label:1, quarter:'Q2', y:77},
                {label:2, quarter:'Q2', y:52},
                {label:3, quarter:'Q2', y:3},

                {label:1, quarter:'Q3', y:65},
                {label:2, quarter:'Q3', y:12},
                {label:3, quarter:'Q3', y:9},

                {label:1, quarter:'Q4', y:77},
                {label:2, quarter:'Q4', y:34},
                {label:3, quarter:'Q4', y:5}
            ];



    var quater = ["Q1","Q2","Q3","Q4"];
    var sum = 0;
    var arr = [];
    for(var i = 0; i<data.length; i++){
        for(var j = 0; j<data.length; j++){
            if (data[j]['quarter'] == quater[i])
                sum = sum + data[j]['y'];
        }
        percent = data[i]['y']/sum;
        //console.log(percent)
        arr.push({
            'label': data[i]['label'],
            'y': data[i]['y'],
            'quater': data[i]['quarter'],
            'percent': percent
        });
        sum = 0;
    }


    console.log(arr);




</script>

1 Comment

While your algorithm works it has an o(n^2) complexity (you are iterating over the array for each entry), the previous answers given to you have a o(n) complexity, and can easily be adjusted to return your final array.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.