Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group objects by multiple properties in array then sum up their values

Grouping elements in array by multiple properties is the closest match to my question as it indeed groups objects by multiple keys in an array. Problem is this solution doesn't sum up the properties value then remove the duplicates, it instead nests all the duplicates in a two-dimensional arrays.

Expected behavior

I have an array of objects which must be grouped by shape and color.

var arr = [     {shape: 'square', color: 'red', used: 1, instances: 1},     {shape: 'square', color: 'red', used: 2, instances: 1},     {shape: 'circle', color: 'blue', used: 0, instances: 0},     {shape: 'square', color: 'blue', used: 4, instances: 4},     {shape: 'circle', color: 'red', used: 1, instances: 1},     {shape: 'circle', color: 'red', used: 1, instances: 0},     {shape: 'square', color: 'blue', used: 4, instances: 5},     {shape: 'square', color: 'red', used: 2, instances: 1} ]; 

Objects in this array are considered duplicates only if both their shape and color are the same. If they are, I want to respectively sum up their used and instances values then delete the duplicates.

So in this example result array may only contain four combinations : square red, square blue, circle red, circle blue

Problem

I tried a simpler approach here:

var arr = [      {shape: 'square', color: 'red', used: 1, instances: 1},      {shape: 'square', color: 'red', used: 2, instances: 1},      {shape: 'circle', color: 'blue', used: 0, instances: 0},      {shape: 'square', color: 'blue', used: 4, instances: 4},      {shape: 'circle', color: 'red', used: 1, instances: 1},      {shape: 'circle', color: 'red', used: 1, instances: 0},      {shape: 'square', color: 'red', used: 4, instances: 4},      {shape: 'square', color: 'red', used: 2, instances: 2}  ];    result = [];    arr.forEach(function (a) {      if ( !this[a.color] && !this[a.shape] ) {          this[a.color] = { color: a.color, shape: a.shape, used: 0, instances: 0 };          result.push(this[a.color]);      }       this[a.color].used += a.used;      this[a.color].instances += a.instances;  }, Object.create(null));    console.log(result);

but it outputs

[{shape: "square", color: "red", used: 11, instances: 9}, {shape: "circle", color: "blue", used: 4, instances: 4}] 

instead of expected result:

[{shape: "square", color: "red", used: 5, instances: 3}, {shape: "circle", color: "red", used: 2, instances: 1}, {shape: "square", color: "blue", used: 11, instances: 9}, {shape: "circle", color: "blue", used: 0, instances: 0}] 

How can I get my function to properly group the objects by shape and color ? i.e. sum up their values and remove the duplicates ?

like image 767
Hal_9100 Avatar asked Oct 17 '17 15:10

Hal_9100


People also ask

How do you find the sum of an array of objects?

To sum a property in an array of objects:Initialize a sum variable, using the let keyword and set it to 0 . Call the forEach() method to iterate over the array. On each iteration, increment the sum variable with the value of the object.

Does include work in array of objects?

There are various methods to check an array includes an object or not. Using includes() Method: If array contains an object/element can be determined by using includes() method. This method returns true if the array contains the object/element else return false.

Can an array have multiple objects?

Answer. Items can be added to an array with the push method. Every array has this method build it, together with many other methods, and can be used to push a new value to the end of the array. In the push method create an object with the keys and the values that you want to add.

Can we have two properties with the same name inside an object?

You cannot. Property keys are unique. Follow TravisJ 's advice. You might want to look up the term 'multimap', too.


1 Answers

Use Array#reduce with a helper object to group similar objects. For each object, check if the combined shape and color exists in the helper. If it doesn't, add to the helper using Object#assign to create a copy of the object, and push to the array. If it does, add it's values to used and instances.

var arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];    var helper = {};  var result = arr.reduce(function(r, o) {    var key = o.shape + '-' + o.color;        if(!helper[key]) {      helper[key] = Object.assign({}, o); // create a copy of o      r.push(helper[key]);    } else {      helper[key].used += o.used;      helper[key].instances += o.instances;    }      return r;  }, []);    console.log(result);

If you can use ES6, you use a Map to collect the values, and then convert it back to an array by spreading the Map#values:

const arr = [{"shape":"square","color":"red","used":1,"instances":1},{"shape":"square","color":"red","used":2,"instances":1},{"shape":"circle","color":"blue","used":0,"instances":0},{"shape":"square","color":"blue","used":4,"instances":4},{"shape":"circle","color":"red","used":1,"instances":1},{"shape":"circle","color":"red","used":1,"instances":0},{"shape":"square","color":"blue","used":4,"instances":5},{"shape":"square","color":"red","used":2,"instances":1}];    const result = [...arr.reduce((r, o) => {    const key = o.shape + '-' + o.color;        const item = r.get(key) || Object.assign({}, o, {      used: 0,      instances: 0    });        item.used += o.used;    item.instances += o.instances;      return r.set(key, item);  }, new Map).values()];    console.log(result);
like image 70
Ori Drori Avatar answered Sep 21 '22 14:09

Ori Drori