Group objects by multiple properties in array then sum up their values
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);
Use this method to specify multiple properties:
public static groupBy(array, f) { let groups = {}; array.forEach(function (o) { var group = JSON.stringify(f(o)); groups[group] = groups[group] || []; groups[group].push(o); }); return Object.keys(groups).map(function (group) { return groups[group]; }) }
Call this method like:
var result = Utils.groupBy(arr, function (item) { return [item.shape, item.color]; });
You could use a hash table and the keys for grouping same groups.
var array = [{ 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 }], hash = Object.create(null), grouped = []; array.forEach(function (o) { var key = ['shape', 'color'].map(function (k) { return o[k]; }).join('|'); if (!hash[key]) { hash[key] = { shape: o.shape, color: o.color, used: 0, instances: 0 }; grouped.push(hash[key]); } ['used', 'instances'].forEach(function (k) { hash[key][k] += o[k]; });});console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }