0

I have an array containing the following objects.

var notTotal = [{"Year":2012,"Value":800579},
                {"Year":2012,"Value":654090},
                {"Year":2012,"Value":758092},
                {"Year":2013,"Value":343928},...More objects.. ]

What im trying to do is traverse this array of objects where only one Year exists instead of multiple and to add up the Values for that year. Using the example above..

var total = [{"Year":2012,"Value":2556689}, 
//Total of first three 2012 assuming there isnt anymore 2012 in the array 
             {"Year":2013,"Value":343928},...]

I have tried something like the following:

for(var i = 0; i < notTotal.length; i++) {
   if (total.includes(notTotal[i].Year || notTotal[i])) {
      //Add the value of the year in notTotal array to the same year in total array
   } else {
   total.push(notTotal[i]); //add year and value if it does not exist to the total array
 }
}

Apologies if this is a duplicate. It seems like a pretty specific question.

Thanks!

Stu
  • 67
  • 6
  • Do you need the final result (`total`) to be an array? This would be pretty easy to implement with a dictionary mapping years to values (since the keys have to be unique). – AstroCB Aug 02 '17 at 15:28
  • See [this answer](https://stackoverflow.com/questions/23521894/how-to-aggregate-objects-properties). – R. Schifini Aug 02 '17 at 15:29
  • It needs to be an array as im plugging it into a D3 Barchart Layout after. – Stu Aug 02 '17 at 15:34
  • Possible duplicate of [Reduce an array of objects and sum property for each distinct object](https://stackoverflow.com/questions/32659248/reduce-an-array-of-objects-and-sum-property-for-each-distinct-object) – A l w a y s S u n n y Aug 02 '17 at 15:41

6 Answers6

2

An easy solution would be to create an object, holding totals by year.

var totalByYear = {};

You can then loop over the array using notTotal.forEach or a for loop as you've shown, adding to the value of the relevant year inside the totalByYear object.

notTotal.forEach(function(ele) { totalByYear[ele.Year] += ele.Value });

This yields an object with year keys and total values, e.g. using your example:

{'2012': 2556689, '2013': 343928 /* other years */}

The desired format (for D3) can then be built from the totalByYear object (and the totals by year printed):

var totals = [];
for (year in totalByYear) {
    console.log('Total for year ' + year + ' is ' + totalByYear[year]);
    //Build the correctly formatted array
    totals.push({ Year: year, Value: totalByYear[year]});
}

//Prints:
//Total for year 2012 is 2556689
//etc.etc.

The totals array will then have the desired format.

Seb Morris
  • 380
  • 1
  • 8
  • Great answer. I, however, need to keep the format of [{"Year": yyyy, "Value": n}... ] as im using this to put in to a D3.js layout afterwards. – Stu Aug 02 '17 at 16:01
  • I've added to the end of the answer, showing how to retain the format for `D3.js` – Seb Morris Aug 02 '17 at 16:08
1

Great question! To explain what's happening in your if (total.includes(notTotal[i].Year || notTotal[i])) is that you are looking through your total array for either just the year, or just an existing notTotal[i] exactly as it is. So your loop is trying to find a value that's exactly 2012 or exactly "Year":2012,"Value":2556689. Ergo, if your total array looked like this:

[{"Year":2012, "Value": 12345}]

your for loop would not find it even though there is an object with 2012 as its year. As for how to fix this, take a look at this previous question!

How to determine if Javascript array contains an object with an attribute that equals a given value?

Hopefully that helps :)

Alice Yu
  • 176
  • 1
  • 8
1

var notTotal = [{"Year":2012,"Value":800579},
                {"Year":2012,"Value":654090},
                {"Year":2012,"Value":758092},
                {"Year":2013,"Value":343928}]
                
var totalObj = notTotal.reduce((sum, obj) => {
  sum[obj.Year] = sum[obj.Year] + obj.Value || obj.Value;
  return sum;
}, {});

// convert total to the format you need;
var total =  Object.entries(totalObj).map(([Year, Value]) => ({Year, Value}))

console.log(total);
Psidom
  • 209,562
  • 33
  • 339
  • 356
  • This is equal to sebs answer, with the difference that he was first and put the effort into a proper explanation... – Jonas Wilms Aug 02 '17 at 15:41
1

one more solution :

function yearlyValueFilter(array){
  var yearlyValue = {}  
  array.forEach( (obj) => { //litterate on the input
    var year = obj.Year   
    var value = obj.Value
    if((year in yearlyValue)){  //if the array with not duplicated years conatins the litteration year just plus that value 
      yearlyValue[year] += value
    }else{      //if not conatins, it gets as a basic value
      yearlyValue[year] = value
    }
  })
  return yearlyValue
}
0

You could built a hash table:

var hash = {},total=[];
for(const {Year,Value} of notTotal){
  if(hash[Year]){
    hash[Year].Value+=Value;
  }else{
    total.push(hash[Year]={Year,Value});
  }
}

In action

Note: object properties are normally not capitalized...

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Appreciated. I'm loading this data in from a D3.json function and the properties are already capitalized. – Stu Aug 02 '17 at 15:58
0

You could use a hash table and check if the year does not exist, then generate a new result set. Then update the total count.

var values = [{ Year: 2012, Value: 800579 }, { Year: 2012, Value: 654090 }, { Year: 2012, Value: 758092 }, { Year: 2013, Value: 343928 }],
    hash = Object.create(null),
    totals = [];
    
values.forEach(function (o) {
    hash[o.Year] || totals.push(hash[o.Year] = { Year: o.Year, Value: 0 });
    hash[o.Year].Value += o.Value;
});

console.log(totals);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392