Inbal Mishory
Inbal Mishory

Reputation: 3

collating same keys in object array and summing up the values javascript

this is my objects array -

var items = [{category: "", sum: 687355.25},
{category: "", sum: 526335.4},
{category: "building, general work and whitewashing", sum: 991844},
{category: "Mining and quarrying of non metal minerals", sum: 566317.64},
{category: "wholesale: marble stone for building", sum: 1100391.64},
{category: "heavy building contracting, infrastructure work contractor", sum: 600000},
{category: "building and construction contractor", sum: 829142.67},
{category: "building and construction contractor", sum: 952417},
{category: "building and construction contractor, general", sum: 731128},
{category: "building and construction contractor, general", sum: 708000},
{category: "building and construction contractor, institutions and public buildings", sum: 542540},
{category: "retail: womens clothing stores", sum: 540000},
{category: "retail: gas stations", sum: 567000},
{category: "financing - banks and foreign currency", sum: 700000},
{category: "financing - banks and checks clearing agencies", sum: 526950},
{category: "real estate projects launching", sum: 1084839.77},
{category: "real estate sales, lease and rental", sum: 650000},
{category: "real estate services purchase agents", sum: 1147500},
{category: "real estate development", sum: 534000},
{category: "services: financial services", sum: 735000}]

I would like to get this result -

modifiedItems = [{category: "", sum: 1213690.65}
{category: "building, general work and whitewashing", sum: 991844},
{category: "Mining and quarrying of non metal minerals", sum: 566317.64},
{category: "wholesale: marble stone for building", sum: 1100391.64},
{category: "heavy building contracting, infrastructure work contractor", sum: 600000},
{category: "building and construction contractor", sum: 1781559.67}
{category: "building and construction contractor, general", sum: 1439128}
{category: "building and construction contractor, institutions and public buildings", sum: 542540},
{category: "retail: womens clothing stores", sum: 540000},
{category: "retail: gas stations", sum: 567000},
{category: "financing - banks and foreign currency", sum: 700000},
{category: "financing - banks and checks clearing agencies", sum: 526950},
{category: "real estate projects launching", sum: 1084839.77},
{category: "real estate sales, lease and rental", sum: 650000},
{category: "real estate services purchase agents", sum: 1147500},
{category: "real estate development", sum: 534000},
{category: "services: financial services", sum: 735000}]

eliminating duplicate keys and summing the value of however many duplicates there are. I know i should use reduce, but just can't figure it out. Please help!

Upvotes: 0

Views: 1245

Answers (3)

jpuntd
jpuntd

Reputation: 902

This can indeed be done using reduce:

results = items.reduce(sumFunction)

It is worth looking at the structure the end result will have. In this case it will be an array of objects. Reduce will build up (or accumulate) this result when it goes through all the items. the initial value to start the accumulation process will be an empty array. We pass this as the second parameter:

results = items.reduce(sumFunction, []);

But how do you write the sumFunction?

The sumFunction will be called with 3 parameters: a variable holding the temporary result (= the accumulator), the current item, and the index of the current item. The accumulator will gradually become more and more like the end result you want: an array of items.

Now we can write what should happen to the accumulator when each step gets passed to it:

function sumFunction (accumulator, currentItem, currentIndex) {
    // look up if the current item is of a category that is already in our end result.
    index = accumulator.findIndex((item) => item.category === currentItem.category)
    if (index < 0) {
        accumulator.push(currentItem); // now item added to the array
    } else {
        accumulator[index].sum += currenItem.sum // update the sum of already existing item
    }
    return accumulator;
}

or if you are using es5:

function sumFunction (accumulator, currentItem, currentIndex) {
    // look up if the current item is of a category that is already in our end result.
    index = accumulator.findIndex(function(item) { return (item.category === currentItem.category);});
    if (index < 0) {
        accumulator.push(currentItem); // now item added to the array
    } else {
        accumulator[index].sum += currenItem.sum // update the sum of already existing item
    }
    return accumulator;
}

Upvotes: 1

nitangle
nitangle

Reputation: 171

I think this should solve the problem. That is what I can come up with right now. I will edit it afterwards if I think of some better way.

var map = {};
var new_items = [];
var length=items.length;
for(var i=0;i<length;i++){
    if(items[i]["category"] in map){
        map[items[i]["category"]]+=items[i]["sum"];
        }
    else{
             map[items[i]["category"]]=items[i]["sum"];         
        }
    }
for(key in map){
    new_items.push({"category":key,"sum":map[key]});
   }

Basically I created a map of the category and sum values and then used it to construct a new array as required.

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191976

You can use Array#reduce with a helper object (dict in the example). The helper object maintains a reference to categories that were already added. When an object contains a new category, add it to the array and the dict object. If the object already exists in dict add it's sum to the category sum.

var items = [{"category":"","sum":687355.25},{"category":"","sum":526335.4},{"category":"building, general work and whitewashing","sum":991844},{"category":"Mining and quarrying of non metal minerals","sum":566317.64},{"category":"wholesale: marble stone for building","sum":1100391.64},{"category":"heavy building contracting, infrastructure work contractor","sum":600000},{"category":"building and construction contractor","sum":829142.67},{"category":"building and construction contractor","sum":952417},{"category":"building and construction contractor, general","sum":731128},{"category":"building and construction contractor, general","sum":708000},{"category":"building and construction contractor, institutions and public buildings","sum":542540},{"category":"retail: womens clothing stores","sum":540000},{"category":"retail: gas stations","sum":567000},{"category":"financing - banks and foreign currency","sum":700000},{"category":"financing - banks and checks clearing agencies","sum":526950},{"category":"real estate projects launching","sum":1084839.77},{"category":"real estate sales, lease and rental","sum":650000},{"category":"real estate services purchase agents","sum":1147500},{"category":"real estate development","sum":534000},{"category":"services: financial services","sum":735000}];

var dict = Object.create(null); // create an empty object

var result = items.reduce(function(arr, o) {
  var current = dict[o.category]; // get the object from dict
  
  if(!current) { // if dict doesn't contain object
    current = Object.assign({}, o); // create a clone of the object - this prevents changing the original object
    
    arr.push(current); // push it to the array
    
    dict[o.category] = current; // add it to dict
  } else { // if dict contains the object
    current.sum += o.sum; // update the sum
  }

  return arr;
}, []);

console.log(result);

Upvotes: 1

Related Questions