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 ?
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.
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.
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.
You cannot. Property keys are unique. Follow TravisJ 's advice. You might want to look up the term 'multimap', too.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With