arnold
arnold

Reputation: 1712

Javascript object sum value based on properties effeicently

I do have three objects inside an array in JS like below

[{"2013-03-02T00:00": 300}, {"2013-03-01T00:00": 200},{"2013-03-02T00:00": 50}]

I want something like below as output from the above array.

[{"2013-03-02T00:00": 350} , {"2013-03-01T00:00": 200}]

It can be done by looping through and adding, are there any efficient way I can do it?

Thanks in advance.

Upvotes: 3

Views: 3955

Answers (3)

HMR
HMR

Reputation: 39280

If you can add the key "date" as another value of the object you can sort and group it, this will be quicker and more optimized when you have more values in the array.

[UPDATE]: fixed a little bug.

var arr = [{"date":"2013-03-02T00:00",val: 300}
 , {"date":"2013-03-01T00:00",val: 200}
 ,{"date":"2013-03-02T00:00",val: 50}];
function groupArray(arr){
  if(arr.length===0){return [];}
  var pref,i;
  // sort by date
  arr.sort(function(a,b){
    return (a.date>b.date)?1:(a.date<b.date)?-1:0; 
  });
  // loop through the array grouping objects by date
  pref=arr[0].date;
  for(i=1;i<arr.length;i++){
    if(arr[i].date===pref){
      //set the total
      arr[i-1].val=arr[i-1].val+arr[i].val;
      //remove the element
      arr.splice(i,1);
      // set i one back
      i--;
    }
    pref=arr[i].date;
  }
  return arr;
}
console.log(groupArray(arr));

A more complicated example is when you dynamically want to provide the key to sort on, in the example above the key is "hard coded" to be date but you maybe need to group on another key value, the following is a piece of code I had laying around that I've simplified to group by one key (original used an array of keys). You can pass a onMerge variable that should be a function that handles how to merge 2 items. This function is not generic and is specific to adding the val properties of the to be merged objects.

var arr = [{"date":"2013-03-02T00:00",val: 300}
 , {"date":"2013-03-01T00:00",val: 200}
 , {"date":"2013-03-01T00:00",val: 200}
 , {"date":"2013-03-01T00:00",val: 200}
 , {"date":"2013-03-01T00:00",val: 200}
 ,{"date":"2013-03-02T00:00",val: 50}];
/**
* @param arr is the array to be merged
* @param key is the key to use for merge 
*   (like date) will merge on items with same 
*   date value
* @param onMerge function to call when 2 items
*    are merged with the 2 items as parameters
**/
function groupArray(arr,key,onMerge){
  if(arr.length===0){return [];}
  var pref,i;
  // sort by key
  arr.sort(function(a,b){
    return (a[key]>b[key])?1:(a[key]<b[key])?-1:0; 
  });
  // loop through the array grouping objects by key
  pref=arr[0][key];
  for(i=1;i<arr.length;i++){
    if(arr[i][key]===pref){
      //merge 2 items, call the onMerge callback
      arr[i-1]=onMerge(arr[i-1],arr[i]);
      //remove the element
      arr.splice(i,1);
      // set i one back
      i--;
    }
    pref=arr[i][key];
  }
  return arr;
}
// functon that will be called when 2 items are merged
// stay will stay and gone will be gone
// this function is specific to your data type
function onMergeCallback(stay,gone){
  stay.val=stay.val+gone.val;
  return stay;
}

console.log(groupArray(arr,"date",onMergeCallback));

Upvotes: 1

Gung Foo
Gung Foo

Reputation: 13558

var myList = [{"2013-03-02T00:00": 300}, {"2013-03-01T00:00": 200},{"2013-03-02T00:00": 50}];
var result = {};
var item = null, key = null;
for(c=0; c<myList.length; c++) {
   item=myList[c];
   key = Object.keys(item)[0];
   item=item[key];

   if(!result[key]) result[key] = item;
   else result[key] += item;
}

console.log(result);

I leave it as an exercise for the reader to put the result into the requested form. (after all you should solve at least some part of your problem yourself :)

Upvotes: 4

Swift
Swift

Reputation: 13188

You might want to have a look at underscore.js, which has implementations for a lot of helpful, efficient functions for manipulating and dealing with data. Specifically, you'd want to have a look at the _.groupBy function:

var data = [{"2013-03-02T00:00": 300}, {"2013-03-01T00:00": 200},{"2013-03-02T00:00": 50}]
_.groupBy(data, function(obj) { return Object.keys(obj)[0]; })

You'd still have to iterate and sum the values, but that's why we have reduce functions!

If you want something more specific to your use case, I would have a look at the source on github.

https://github.com/documentcloud/underscore

Upvotes: 0

Related Questions