Merge objects with same property together [duplicate] - javascript

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 ?

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 can use reduce() to create one object of unique shape|color properties and Object.values() to return array of those values.
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 result = Object.values(arr.reduce(function(r, e) {
var key = e.shape + '|' + e.color;
if (!r[key]) r[key] = e;
else {
r[key].used += e.used;
r[key].instances += e.instances
}
return r;
}, {}))
console.log(result)

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; }

ES6 answers as requisted by user:
// To call this function:
// const result = this.toolBox.multipleGroupByArray(
// dataArray, (property: IProperty) => [property.prop1, property.prop2, property.prop3]);
multipleGroupByArray(dataArray, groupPropertyArray) {
const groups = {};
dataArray.forEach(item => {
const group = JSON.stringify(groupPropertyArray(item));
groups[group] = groups[group] || [];
groups[group].push(item);
});
return Object.keys(groups).map(function(group) {
return groups[group];
});
}

Here is a more general grouping and summing function that accepts an array of objects, an array of keys to group by, and an array of keys to sum.
function groupAndSum(arr, groupKeys, sumKeys){
return Object.values(
arr.reduce((acc,curr)=>{
const group = groupKeys.map(k => curr[k]).join('-');
acc[group] = acc[group] || Object.fromEntries(
groupKeys.map(k => [k, curr[k]]).concat(sumKeys.map(k => [k, 0])));
sumKeys.forEach(k => acc[group][k] += curr[k]);
return acc;
}, {})
);
}
Demo:
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}
];
function groupAndSum(arr, groupKeys, sumKeys){
return Object.values(
arr.reduce((acc,curr)=>{
const group = groupKeys.map(k => curr[k]).join('-');
acc[group] = acc[group] || Object.fromEntries(groupKeys.map(k => [k, curr[k]]).concat(sumKeys.map(k => [k, 0])));
sumKeys.forEach(k => acc[group][k] += curr[k]);
return acc;
}, {})
);
}
const res = groupAndSum(arr, ['shape', 'color'], ['used', 'instances']);
console.log(res);

If you want groupBy keys as per conditional fields then here is the modification for #Abbes answer:
function groupBy(array, f) {
let groups = {};
array.forEach((o) => {
var group = f(o).join('-');
groups[group] = groups[group] || [];
groups[group].push(o);
});
return groups;
}
And use it like:
groupBy(connectedServers, (item) => {
return [item.key1, item.key2];
});

In case if you need an array of 'used' and 'instances' based on the colour or shape property; then you can use this code .
(PS : I know this is not what you are looking for but in future it may help someone. Also am reusing Nenand's code for this purpose . If the code is useful to you just thank him )
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, YourArrayName : [] };
grouped.push(hash[key]);
}
['used'].forEach(function (k) { hash[key]['YourArrayName'].push({ used : o['used'], instances : o['instances'] }) });
});
console.log(grouped);
The output will be in like

I have a suggestion for you.
If you want to make it easier to do, you try the Underscore library : http://underscorejs.org/
I tried quickly to use it and got the right result :
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 byshape = _.groupBy(arr, 'shape');
var bycolor = _.map(byshape, function(array) {
return _.groupBy(array, 'color')
});
var output = [];
_.each(bycolor, function(arrayOfShape) {
_.each(arrayOfShape, function(arrayOfColor) {
var computedItem = {shape: "", color: "", used: 0, instances: 0};
_.each(arrayOfColor, function(item) {
computedItem.shape = item.shape;
computedItem.color = item.color;
computedItem.used += item.used;
computedItem.instances += item.instances;
});
output.push(computedItem);
});
});
console.log(output);
http://jsfiddle.net/oLyzdoo7/
This solution groups first data, then you can do what you want after, for example, compute data as ypur wish.
Maybe you can optimize it, let me know if you need more help

/**
* Groups an array of objects with multiple properties.
*
* #param {Array} array: the array of objects to group
* #param {Array} props: the properties to groupby
* #return {Array} an array of arrays with the grouped results
*/
const groupBy = ({ Group: array, By: props }) => {
getGroupedItems = (item) => {
returnArray = [];
let i;
for (i = 0; i < props.length; i++) {
returnArray.push(item[props[i]]);
}
return returnArray;
};
let groups = {};
let i;
for (i = 0; i < array.length; i++) {
const arrayRecord = array[i];
const group = JSON.stringify(getGroupedItems(arrayRecord));
groups[group] = groups[group] || [];
groups[group].push(arrayRecord);
}
return Object.keys(groups).map((group) => {
return groups[group];
});
};
Example:
Assume that we have an array of objects. Each object contains info about a person and the money that possess. We want to sum up the money for all persons with the same Nationality and with the same gender.
const data = [
{Name: 'George', Surname: 'Best', Country: 'Great Britain', Gender: 'Male', Money:8000},
{Name: 'Orion', Surname: 'Papathanasiou', Country: 'Greece', Gender: 'Male', Money: 2000},
{Name: 'Mairy', Surname: 'Wellbeck', Country: 'Great Britain', Gender: 'Female', Money:5000},
{Name: 'Thanasis', Surname: 'Papathanasiou', Country: 'Greece',Gender: 'Male', Money: 3200},
{Name: 'George', Surname: 'Washington', Country: 'Great Britain', Gender: 'Male',Money:4200},
{Name: 'Orfeas', Surname: 'Kalaitzis', Country: 'Greece', Gender: 'Male', Money: 7643},
{Name: 'Nick', Surname: 'Wellington', Country: 'USA', Gender: 'Male', Money:1000},
{Name: 'Kostas', Surname: 'Antoniou', Country: 'Greece', Gender: 'Male', Money: 8712},
{Name: 'John', Surname: 'Oneal', Country: 'USA', Gender: 'Male', Money:98234},
{Name: 'Paulos', Surname: 'Stamou', Country: 'Greece', Gender: 'Male', Money: 3422},
{Name: 'Soula', Surname: 'Spuropoulou', Country: 'Greece', Gender: 'Female', Money:400},
{Name: 'Paul', Surname: 'Pierce', Country: 'USA', Gender: 'Male',Money: 13000},
{Name: 'Helen', Surname: 'Smith', Country: 'Great Britain', Gender: 'Female', Money:1000},
{Name: 'Cathrine', Surname: 'Bryant', Country: 'Great Britain', Gender: 'Female', Money: 8712},
{Name: 'Jenny', Surname: 'Scalabrini', Country: 'USA', Gender: 'Female', Money:92214}];
const groupByProperties = ['Country', 'Gender'];
Calling the function:
const groupResult = groupBy( {Group: data, By: groupByProperties} );
The group result is:
(6) [Array(2), Array(5), Array(3), Array(3), Array(1), Array(1)]
0: Array(2)
0: {Name: "George", Surname: "Best", Country: "Great Britain", Gender: "Male", Money: 8000}
1: {Name: "George", Surname: "Washington", Country: "Great Britain", Gender: "Male", Money: 4200}
length: 2
__proto__: Array(0)
1: Array(5)
0: {Name: "Orion", Surname: "Papathanasiou", Country: "Greece", Gender: "Male", Money: 2000}
1: {Name: "Thanasis", Surname: "Papathanasiou", Country: "Greece", Gender: "Male", Money: 3200}
2: {Name: "Orfeas", Surname: "Kalaitzis", Country: "Greece", Gender: "Male", Money: 7643}
3: {Name: "Kostas", Surname: "Antoniou", Country: "Greece", Gender: "Male", Money: 8712}
4: {Name: "Paulos", Surname: "Stamou", Country: "Greece", Gender: "Male", Money: 3422}
length: 5
__proto__: Array(0)
2: Array(3)
0: {Name: "Mairy", Surname: "Wellbeck", Country: "Great Britain", Gender: "Female", Money: 5000}
1: {Name: "Helen", Surname: "Smith", Country: "Great Britain", Gender: "Female", Money: 1000}
2: {Name: "Cathrine", Surname: "Bryant", Country: "Great Britain", Gender: "Female", Money: 8712}
length: 3
__proto__: Array(0)
3: Array(3)
0: {Name: "Nick", Surname: "Wellington", Country: "USA", Gender: "Male", Money: 1000}
1: {Name: "John", Surname: "Oneal", Country: "USA", Gender: "Male", Money: 98234}
2: {Name: "Paul", Surname: "Pierce", Country: "USA", Gender: "Male", Money: 13000}
length: 3
__proto__: Array(0)
4: Array(1)
0: {Name: "Soula", Surname: "Spuropoulou", Country: "Greece", Gender: "Female", Money: 400}
length: 1
__proto__: Array(0)
5: Array(1)
0: {Name: "Jenny", Surname: "Scalabrini", Country: "USA", Gender: "Female", Money: 92214}
length: 1
__proto__: Array(0)
length: 6
__proto__: Array(0)
So, we got 6 arrays. Each array is grouped by Country and by Gender
Iterating over each array, we can sum up the money!
const groupBy = ({ Group: array, By: props }) => {
getGroupedItems = (item) => {
returnArray = [];
let i;
for (i = 0; i < props.length; i++) {
returnArray.push(item[props[i]]);
}
return returnArray;
};
let groups = {};
let i;
for (i = 0; i < array.length; i++) {
const arrayRecord = array[i];
const group = JSON.stringify(getGroupedItems(arrayRecord));
groups[group] = groups[group] || [];
groups[group].push(arrayRecord);
}
return Object.keys(groups).map((group) => {
return groups[group];
});
};
const data = [
{Name: 'George', Surname: 'Best', Country: 'Great Britain', Gender: 'Male', Money:8000},
{Name: 'Orion', Surname: 'Papathanasiou', Country: 'Greece', Gender: 'Male', Money: 2000},
{Name: 'Mairy', Surname: 'Wellbeck', Country: 'Great Britain', Gender: 'Female', Money:5000},
{Name: 'Thanasis', Surname: 'Papathanasiou', Country: 'Greece',Gender: 'Male', Money: 3200},
{Name: 'George', Surname: 'Washington', Country: 'Great Britain', Gender: 'Male',Money:4200},
{Name: 'Orfeas', Surname: 'Kalaitzis', Country: 'Greece', Gender: 'Male', Money: 7643},
{Name: 'Nick', Surname: 'Wellington', Country: 'USA', Gender: 'Male', Money:1000},
{Name: 'Kostas', Surname: 'Antoniou', Country: 'Greece', Gender: 'Male', Money: 8712},
{Name: 'John', Surname: 'Oneal', Country: 'USA', Gender: 'Male', Money:98234},
{Name: 'Paulos', Surname: 'Stamou', Country: 'Greece', Gender: 'Male', Money: 3422},
{Name: 'Soula', Surname: 'Spuropoulou', Country: 'Greece', Gender: 'Female', Money:400},
{Name: 'Paul', Surname: 'Pierce', Country: 'USA', Gender: 'Male',Money: 13000},
{Name: 'Helen', Surname: 'Smith', Country: 'Great Britain', Gender: 'Female', Money:1000},
{Name: 'Cathrine', Surname: 'Bryant', Country: 'Great Britain', Gender: 'Female', Money: 8712},
{Name: 'Jenny', Surname: 'Scalabrini', Country: 'USA', Gender: 'Female', Money:92214}];
const groupByProperties = ['Country', 'Gender'];
const groupResult = groupBy( {Group: data, By: groupByProperties} );
console.log(groupResult);

I found some of these answers a little hard to reuse so here is a reusable function that you can pass in what keys you want your grouping to use.
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}
];
const groupByMultipleKeys = (items, keys) =>
items.reduce((acc, item) => {
const isExistingItem = acc
.flatMap(accItem => accItem)
.find(accItem =>
keys.every(key => accItem[key] === item[key])
)
if (isExistingItem) {
return acc;
}
const allRelatedItems = items.filter(ungroupedItem =>
keys.every(key => ungroupedItem[key] === item[key])
)
acc.push(allRelatedItems)
return acc
}, [])
const groupedItem = groupByMultipleKeys(arr, ['shape', 'color'])
console.log('groupedItem', groupedItem)
List item

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}
];
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);
**Output:**
[
{
"color": "red",
"shape": "square",
"used": 11,
"instances": 9
},
{
"color": "blue",
"shape": "circle",
"used": 4,
"instances": 4
}
]
thats all perfetcly working.
Enjoy your coding....

1.sumkeys
3.groupkeys
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}
];
function groupbykeys(arr, groupKeys, sumKeys){
var hash = Object.create(null),
grouped = [];
arr.forEach(function (o) {
var key = groupKeys.map(function (k) { return o[k]; }).join('|');
if (!hash[key]) {
hash[key] = Object.keys(o).reduce((result, key)=> {
result[key]=o[key];
if(sumKeys.includes(key))
result[key]=0;
return result;
}, { }); //map_(o) //{ shape: o.shape, color: o.color, used: 0, instances: 0 };
grouped.push(hash[key]);
}
sumKeys.forEach(function (k) { hash[key][k] += o[k]; });
});
return grouped;
}
var result=groupbykeys(arr,['shape','color'],['used','instances']);
console.log(result)

Aggregate into an object keyed by the unique combination of shape and color with reduce then take the values out of it:
const aggregate = xs => Object.values(
xs.reduce((acc, {shape, color, used, instances}) => {
const key = shape + color;
acc[key] ??= {shape, color, used: 0, instances: 0};
acc[key].used += used;
acc[key].instances += instances;
return acc;
}, {})
);
console.log(aggregate(arr));
<script>
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}];
</script>

Much more compact ES6 version, which uses JSON.stringify to ensure proper separation between the properties that are being grouped by.
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}];
let grouped = Object.values(arr.reduce((a,c)=> {
let i = a[JSON.stringify([c.shape, c.color])] ??= {...c, used: 0, instances: 0};
i.used += c.used; i.instances += c.instances; return a;
} , {}));
console.log(grouped);
Or, to make it easier to specify arbitrary grouping properties and summing properties:
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}];
function groupAndSum(arr, groupProps, sumProps) {
return Object.values(arr.reduce((a,c)=> {
let i = a[JSON.stringify(groupProps.map(p=>[p,c[p]]))] ??=
{...c, ...Object.fromEntries(sumProps.map(p=>[p, 0]))};
sumProps.forEach(p=>i[p]+=c[p]); return a;
} , {}));
}
console.log(groupAndSum(arr, ['shape', 'color'], ['used', 'instances']));

const v = [{"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}];
function groupBy<T>(
arr: T[],
vInitial: Partial<T>,
fn: (curr: T, acc: T) => void,
...args: Array<keyof T>
) {
return Array.from(
arr
.reduce((r, o) => {
const key = args.map((k) => o[k]).join("|");
const ob = Object.assign({}, o);
const obj = {};
for (const key of Object.keys(ob)) {
if (
vInitial != null &&
vInitial.hasOwnProperty(key) &&
vInitial[key] != null
) {
obj[key] = vInitial[key];
}
}
const item = r.get(key) ?? Object.assign({}, o, obj);
fn(item, o);
return r.set(key, item);
}, new Map<string, T>())
.values(),
);
}
console.log(groupBy(
v,
{},
(item, o) => {
item.used += o.used;
item.instances += o.instances;
},
'shape', 'color'));

Related

Array transformation in JavaScript [duplicate]

I have a heavy array like this:
[
{Id: 1, Name: 'Red', optionName: 'Color'},
{Id: 2, Name: 'Yellow', optionName: 'Color'},
{Id: 3, Name: 'Blue', optionName: 'Color'},
{Id: 4, Name: 'Green', optionName: 'Color'},
{Id: 7, Name: 'Black', optionName: 'Color'},
{Id: 8, Name: 'S', optionName: 'Size'},
{Id: 11, Name: 'M', optionName: 'Size'},
{Id: 12, Name: 'L', optionName: 'Size'},
{Id: 13, Name: 'XL', optionName: 'Size'},
{Id: 14, Name: 'XXL', optionName: 'Size'}
]
What I need to do is to group them by optionName and have two row in the main array like this:
[
{
Name: 'Color',
Data:[{Id: 1, Name: 'Red'},
{Id: 2, Name: 'Yellow'},
{Id: 3, Name: 'Blue'},
{Id: 4, Name: 'Green'},
{Id: 7, Name: 'Black'}]
}, {
Name: 'Size',
Data:[{Id: 8, Name: 'S'},
{Id: 11, Name: 'M'},
{Id: 12, Name: 'L'},
{Id: 13, Name: 'XL'},
{Id: 14, Name: 'XXL'}]
}
]
How to do it in javascript?
This is a snippet I wrote for these kind of situations. You can add this functionality to all of your arrays:
Object.defineProperty(Array.prototype, 'group', {
enumerable: false,
value: function (key) {
var map = {};
this.forEach(function (e) {
var k = key(e);
map[k] = map[k] || [];
map[k].push(e);
});
return Object.keys(map).map(function (k) {
return {key: k, data: map[k]};
});
}
});
You can use it like this. You can just pass a function which defines how you want to group your data.
var newArray = arr.group(function (item) {
return item.optionName;
});
Working Fiddle
If you need, you can replace {key: k, data: map[k]} with {Name: k, Data: map[k]}.
This is also more compact ES6 version of the code above:
Object.defineProperty(Array.prototype, 'group', {
enumerable: false,
value: function (key) {
let map = {};
this.map(e => ({k: key(e), d: e})).forEach(e => {
map[e.k] = map[e.k] || [];
map[e.k].push(e.d);
});
return Object.keys(map).map(k => ({key: k, data: map[k]}));
}
});
Use it like this:
let newArray = arr.group(item => item.optionName))
An ES6 solution using Map object:
function groupBy(arr, key) {
return arr.reduce(
(sum, item) => {
const groupByVal = item[key];
groupedItems = sum.get(groupByVal) || [];
groupedItems.push(item);
return sum.set(groupByVal, groupedItems);
},
new Map()
);
}
var Data = [
{ Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
document.getElementById("showArray").innerHTML =JSON.stringify([...groupBy(Data, 'optionName')], null, 4);
<pre id="showArray"></pre>
You can use reduce to get the resultset you need:
var result = list.reduce(function(memo, item) {
if (item.optionName === 'Color') {
memo[0].Data.push(
Id: item.Id,
Name: item.Name
});
}
if (item.optionName === 'Size') {
memo[1].Data.push({
Id: item.Id,
Name: item.Name
});
}
return memo;
}, [{ Name: 'Color', Data: [] }, { Name: 'Size', Data: [] }]);
variable list is your first list.
Hope this helps.
This is a snippet I wrote for kind of my situation in my application functionality of all arrays. This snippet code is use in node js application. All the above is is given solution but I was finding some problem in server side in node js.
This snippet is user full me....
var Data= [
{ Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
function groupBy(arr, key) {
var newArr = [],
types = {},
newItem, i, j, cur;
for (i = 0, j = arr.length; i < j; i++) {
cur = arr[i];
if (!(cur[key] in types)) {
types[cur[key]] = { type: cur[key], data: [] };
newArr.push(types[cur[key]]);
}
types[cur[key]].data.push(cur);
}
return newArr;
}
I use it like this. I just pass a function which defines how you want to group our data.
filterData= groupBy(Data,'optionName');
Result of this snippet of code output.....
[
{"type":"Color","data":[{"Id":1,"Name":"Red","optionName":"Color"},
{"Id":2,"Name":"Yellow","optionName":"Color"},
{"Id":3,"Name":"Blue","optionName":"Color"},
{"Id":4,"Name":"Green","optionName":"Color"},
{"Id":7,"Name":"Black","optionName":"Color"}]},
{"type":"Size","data":[{"Id":8,"Name":"S","optionName":"Size"},
{"Id":11,"Name":"M","optionName":"Size"},
{"Id":12,"Name":"L","optionName":"Size"},
{"Id":13,"Name":"XL","optionName":"Size"},
{"Id":14,"Name":"XXL","optionName":"Size"}]}
]
Show on fiddle
var originalList = [ { Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
var output = [{ Name: "Color", Data: [] },{ Name: "Size", Data: [] }] ;
originalList.map(function(entry){
if ( entry.optionName === "Color") output[0].Data.push({ Id: entry.Id, Name: entry.Name });
if ( entry.optionName === "Size") output[1].Data.push({ Id: entry.Id, Name: entry.Name });
});
'use strict'
let l = [ { Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
let color = [];
let size = [];
l.forEach(element => {
if (element['optionName'] === 'Color') {
color.push({'Id': element.Id, 'Name': element.Name});
} else {
size.push({'Id': element.Id, 'Name': element.Name});
}
});
console.log(color);
console.log(size);
You can try this method.
All of the answers lead to the same result, so it all comes down to a personal preference (or company guidelines) on how to tackle this.
// ES5 (traditional javascript) version
function groupByOptionName(list, optionName) {
return list
// filter out any item whose optionName does not match the desired name
.filter(function(item) {
return item.optionName === optionName;
})
// map the item into the desired shape
// (appears to be everything except optionName itself
.map(function(item) {
return {
Id: item.Id,
Name: item.Name
};
})
}
// ES2015/ES6 version
function groupByOptionName(list, optionName) {
return list
// filter out any item whose optionName does not match the desired name
.filter(item => item.optionName === optionName)
// map the item into the desired shape
// (appears to be everything except optionName itself
.map(item => {
Id: item.Id,
Name: item.Name
});
}
This function would let you program the desired result as follows:
var output = [
{Name: 'Color', Data: groupByOptionName(list, 'Color')},
{Name: 'Size', Data: groupByOptionName(list, 'Size')},
];
// the ES2015/ES6 version of this code would replace var with let
While the code itself differs, it is much like the other answers, with only a variation on the steps needed.
One could also opt to leave out any hardcoded option names (Color and Size) by extracting those aswel, this would allow for a more dynamic input, but could also introduce more processing that actually needed.
// ES5 (traditional javascript) version
function getOptionNames(list) {
return list
// map the array into a list of optionNames
.map(function(item) {
return item.optionName;
})
// remove duplicates
.filter(function(item, index, all) {
return all.indexOf(item) === index;
});
}
// ES2015/ES6 version (does exactly the same as the one above)
function getOptionNames(list) {
return list
// map the array into a list of optionNames
.map(item => item.optionName)
// remove duplicates
.filter((item, index, all) => all.indexOf(item) === index);
}
Which allows the result to be fully based on the input data:
// ES5 (traditional javascript) version
var output = getOptionNames(list)
// map the names into the desired structure
.map(function(buffer, name) {
return {
Name: name,
Data: groupByOptionName(list, name)
};
});
// ES2015/ES6 version (does exactly the same as the one above)
var output = getOptionNames(list)
// map the names into the desired structure
.map((buffer, name) => {
Name: name,
Data: groupByOptionName(list, name)
});
By writing all of the data-mangling steps in short consice steps you'd do yourself (especially your future self) a favor if this code ever needs to be adjusted.
If the data set really is heavy (in terms of a lot of data), you must also make sure to keep the number of copies you keep in memory limited. For example, if you never need the original dataset, make sure it can be garbage collected (by not having a variable containing it outside the scope where you receive the data)
Usage:
groupValues([
{ color: 'blue', value: 100 },
{ color: 'blue', value: 75 },
{ color: 'yellow', value: 50 },
{ color: 'yellow', value: 25 }
], 'color')
Result:
[
[{ color: 'blue', value: 100 }, { color: 'blue', value: 75 }],
[{ color: 'yellow', value: 50 }, { color: 'yellow', value: 25 }]
]
Function:
const groupValues = function(arr, key) {
const mapped = {}
arr.forEach(el => {
const actualKey = el[key]
if(!mapped.hasOwnProperty(actualKey)) mapped[actualKey] = []
mapped[actualKey].push(el)
})
return Object.keys(mapped).map(el => mapped[el])
}

javascript: rearrange array of objects with arrays, injecting objects into groups along with their parameters [duplicate]

I have a heavy array like this:
[
{Id: 1, Name: 'Red', optionName: 'Color'},
{Id: 2, Name: 'Yellow', optionName: 'Color'},
{Id: 3, Name: 'Blue', optionName: 'Color'},
{Id: 4, Name: 'Green', optionName: 'Color'},
{Id: 7, Name: 'Black', optionName: 'Color'},
{Id: 8, Name: 'S', optionName: 'Size'},
{Id: 11, Name: 'M', optionName: 'Size'},
{Id: 12, Name: 'L', optionName: 'Size'},
{Id: 13, Name: 'XL', optionName: 'Size'},
{Id: 14, Name: 'XXL', optionName: 'Size'}
]
What I need to do is to group them by optionName and have two row in the main array like this:
[
{
Name: 'Color',
Data:[{Id: 1, Name: 'Red'},
{Id: 2, Name: 'Yellow'},
{Id: 3, Name: 'Blue'},
{Id: 4, Name: 'Green'},
{Id: 7, Name: 'Black'}]
}, {
Name: 'Size',
Data:[{Id: 8, Name: 'S'},
{Id: 11, Name: 'M'},
{Id: 12, Name: 'L'},
{Id: 13, Name: 'XL'},
{Id: 14, Name: 'XXL'}]
}
]
How to do it in javascript?
This is a snippet I wrote for these kind of situations. You can add this functionality to all of your arrays:
Object.defineProperty(Array.prototype, 'group', {
enumerable: false,
value: function (key) {
var map = {};
this.forEach(function (e) {
var k = key(e);
map[k] = map[k] || [];
map[k].push(e);
});
return Object.keys(map).map(function (k) {
return {key: k, data: map[k]};
});
}
});
You can use it like this. You can just pass a function which defines how you want to group your data.
var newArray = arr.group(function (item) {
return item.optionName;
});
Working Fiddle
If you need, you can replace {key: k, data: map[k]} with {Name: k, Data: map[k]}.
This is also more compact ES6 version of the code above:
Object.defineProperty(Array.prototype, 'group', {
enumerable: false,
value: function (key) {
let map = {};
this.map(e => ({k: key(e), d: e})).forEach(e => {
map[e.k] = map[e.k] || [];
map[e.k].push(e.d);
});
return Object.keys(map).map(k => ({key: k, data: map[k]}));
}
});
Use it like this:
let newArray = arr.group(item => item.optionName))
An ES6 solution using Map object:
function groupBy(arr, key) {
return arr.reduce(
(sum, item) => {
const groupByVal = item[key];
groupedItems = sum.get(groupByVal) || [];
groupedItems.push(item);
return sum.set(groupByVal, groupedItems);
},
new Map()
);
}
var Data = [
{ Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
document.getElementById("showArray").innerHTML =JSON.stringify([...groupBy(Data, 'optionName')], null, 4);
<pre id="showArray"></pre>
You can use reduce to get the resultset you need:
var result = list.reduce(function(memo, item) {
if (item.optionName === 'Color') {
memo[0].Data.push(
Id: item.Id,
Name: item.Name
});
}
if (item.optionName === 'Size') {
memo[1].Data.push({
Id: item.Id,
Name: item.Name
});
}
return memo;
}, [{ Name: 'Color', Data: [] }, { Name: 'Size', Data: [] }]);
variable list is your first list.
Hope this helps.
This is a snippet I wrote for kind of my situation in my application functionality of all arrays. This snippet code is use in node js application. All the above is is given solution but I was finding some problem in server side in node js.
This snippet is user full me....
var Data= [
{ Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
function groupBy(arr, key) {
var newArr = [],
types = {},
newItem, i, j, cur;
for (i = 0, j = arr.length; i < j; i++) {
cur = arr[i];
if (!(cur[key] in types)) {
types[cur[key]] = { type: cur[key], data: [] };
newArr.push(types[cur[key]]);
}
types[cur[key]].data.push(cur);
}
return newArr;
}
I use it like this. I just pass a function which defines how you want to group our data.
filterData= groupBy(Data,'optionName');
Result of this snippet of code output.....
[
{"type":"Color","data":[{"Id":1,"Name":"Red","optionName":"Color"},
{"Id":2,"Name":"Yellow","optionName":"Color"},
{"Id":3,"Name":"Blue","optionName":"Color"},
{"Id":4,"Name":"Green","optionName":"Color"},
{"Id":7,"Name":"Black","optionName":"Color"}]},
{"type":"Size","data":[{"Id":8,"Name":"S","optionName":"Size"},
{"Id":11,"Name":"M","optionName":"Size"},
{"Id":12,"Name":"L","optionName":"Size"},
{"Id":13,"Name":"XL","optionName":"Size"},
{"Id":14,"Name":"XXL","optionName":"Size"}]}
]
Show on fiddle
var originalList = [ { Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
var output = [{ Name: "Color", Data: [] },{ Name: "Size", Data: [] }] ;
originalList.map(function(entry){
if ( entry.optionName === "Color") output[0].Data.push({ Id: entry.Id, Name: entry.Name });
if ( entry.optionName === "Size") output[1].Data.push({ Id: entry.Id, Name: entry.Name });
});
'use strict'
let l = [ { Id: 1, Name: 'Red', optionName: 'Color' },
{ Id: 2, Name: 'Yellow', optionName: 'Color' },
{ Id: 3, Name: 'Blue', optionName: 'Color' },
{ Id: 4, Name: 'Green', optionName: 'Color' },
{ Id: 7, Name: 'Black', optionName: 'Color' },
{ Id: 8, Name: 'S', optionName: 'Size' },
{ Id: 11, Name: 'M', optionName: 'Size' },
{ Id: 12, Name: 'L', optionName: 'Size' },
{ Id: 13, Name: 'XL', optionName: 'Size' },
{ Id: 14, Name: 'XXL', optionName: 'Size' } ];
let color = [];
let size = [];
l.forEach(element => {
if (element['optionName'] === 'Color') {
color.push({'Id': element.Id, 'Name': element.Name});
} else {
size.push({'Id': element.Id, 'Name': element.Name});
}
});
console.log(color);
console.log(size);
You can try this method.
All of the answers lead to the same result, so it all comes down to a personal preference (or company guidelines) on how to tackle this.
// ES5 (traditional javascript) version
function groupByOptionName(list, optionName) {
return list
// filter out any item whose optionName does not match the desired name
.filter(function(item) {
return item.optionName === optionName;
})
// map the item into the desired shape
// (appears to be everything except optionName itself
.map(function(item) {
return {
Id: item.Id,
Name: item.Name
};
})
}
// ES2015/ES6 version
function groupByOptionName(list, optionName) {
return list
// filter out any item whose optionName does not match the desired name
.filter(item => item.optionName === optionName)
// map the item into the desired shape
// (appears to be everything except optionName itself
.map(item => {
Id: item.Id,
Name: item.Name
});
}
This function would let you program the desired result as follows:
var output = [
{Name: 'Color', Data: groupByOptionName(list, 'Color')},
{Name: 'Size', Data: groupByOptionName(list, 'Size')},
];
// the ES2015/ES6 version of this code would replace var with let
While the code itself differs, it is much like the other answers, with only a variation on the steps needed.
One could also opt to leave out any hardcoded option names (Color and Size) by extracting those aswel, this would allow for a more dynamic input, but could also introduce more processing that actually needed.
// ES5 (traditional javascript) version
function getOptionNames(list) {
return list
// map the array into a list of optionNames
.map(function(item) {
return item.optionName;
})
// remove duplicates
.filter(function(item, index, all) {
return all.indexOf(item) === index;
});
}
// ES2015/ES6 version (does exactly the same as the one above)
function getOptionNames(list) {
return list
// map the array into a list of optionNames
.map(item => item.optionName)
// remove duplicates
.filter((item, index, all) => all.indexOf(item) === index);
}
Which allows the result to be fully based on the input data:
// ES5 (traditional javascript) version
var output = getOptionNames(list)
// map the names into the desired structure
.map(function(buffer, name) {
return {
Name: name,
Data: groupByOptionName(list, name)
};
});
// ES2015/ES6 version (does exactly the same as the one above)
var output = getOptionNames(list)
// map the names into the desired structure
.map((buffer, name) => {
Name: name,
Data: groupByOptionName(list, name)
});
By writing all of the data-mangling steps in short consice steps you'd do yourself (especially your future self) a favor if this code ever needs to be adjusted.
If the data set really is heavy (in terms of a lot of data), you must also make sure to keep the number of copies you keep in memory limited. For example, if you never need the original dataset, make sure it can be garbage collected (by not having a variable containing it outside the scope where you receive the data)
Usage:
groupValues([
{ color: 'blue', value: 100 },
{ color: 'blue', value: 75 },
{ color: 'yellow', value: 50 },
{ color: 'yellow', value: 25 }
], 'color')
Result:
[
[{ color: 'blue', value: 100 }, { color: 'blue', value: 75 }],
[{ color: 'yellow', value: 50 }, { color: 'yellow', value: 25 }]
]
Function:
const groupValues = function(arr, key) {
const mapped = {}
arr.forEach(el => {
const actualKey = el[key]
if(!mapped.hasOwnProperty(actualKey)) mapped[actualKey] = []
mapped[actualKey].push(el)
})
return Object.keys(mapped).map(el => mapped[el])
}

How to find matching colors in an array of objects?

I have this list of colors:
colors = ['red', 'yellow', 'white'];
This is my array objects:
products = [
{id: 1, ​name: "Apple", color: ['Brown','black'] },
{id: 2, name: "Orange", color: ['red']},
​{id: 3, name: "Grape", color: ['red','white']},
​ {id: 4, name: "Banana", color: ['yellow']},
{id: 5, name: "Mandarin", color: ['blue']}
];
I want to return an array of the matching colors like so: [Orange,Grape,Banana];
Try this, it traverses through product lists and checks if the color list contains one of the colors
const list = products.filter((obj)=>{
return obj.color.some(c=>colors.includes(c))
});
Try using a filter and a map...
const colors = ['red', 'yellow', 'white'];
const products = [
{id: 1, name: "Apple", color: ['Brown','black'] },
{id: 2, name: "Orange", color: ['red']},
{id: 3, name: "Grape", color: ['red','white']},
{id: 4, name: "Banana", color: ['yellow']},
{id: 5, name: "Mandarin", color: ['blue']}
];
const arr = products.filter(product => {
return product.color.some(color => colors.some(c => c === color))
}).map(product => product.name);
console.log(arr);
This should be what you're looking for as it produces the correct list of items as requested in the question.
This runs 2 filters to find matching colors
products.filter(e => e.color.filter( c=> colors.includes(c)).length>0)
colors = ['red', 'yellow', 'white']
products = [
{id: 1, name: "Apple", color: ['Brown','black'] },
{id: 2, name: "Orange", color: ['red']},
{id: 3, name: "Grape", color: ['red','white']},
{id: 4, name: "Banana", color: ['yellow']},
{id: 5, name: "Mandarin", color: ['blue']}
]
let filtered = products.filter(e => e.color.filter( c=> colors.includes(c)).length>0)
console.log(filtered)
let requiredArray = products.filter(({ color }) => colors.some(item => color.includes(item)));
console.log(requiredArray);

How to get an object's value from another object's key?

Having:
const colors = [
{id: 1, name: "blue"},
{id: 2, name: "red"},
{id: 3, name: "green"}
];
And:
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
How can I reference the colors in shirts by the id in colors? I need to end up with shirt.color = blue.
I'm thinking about something like this, but can't get it right:
if (shirt.color === color.id) {
shirt.color = color.name;
}
Thanks in advance!
You can use forEach
const colors = [
{id: 1, name: "blue"},
{id: 2, name: "red"},
{id: 3, name: "green"}
];
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
shirts.forEach(e=>{
colors.forEach(c=>{
if(e.color == c.id ){
e.color = c.name;
}
})
})
console.log(shirts);
You can use Array.map() for that to loop over the colors and then use Array.filter() to get all the shirts that matches color.id === shirt.color. With that you can then assign that color name to all the shirt object as expected.
const colors = [
{id: 1, name: "blue"},
{id: 2, name: "red"},
{id: 3, name: "green"}
];
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
colors.map((color) => {
let matchShirt = shirts.filter((shirt) => color.id === shirt.color);
matchShirt.forEach((shirt) => shirt.color = color.name);
});
console.log(shirts);
You could take a Map and create new objects with the color name.
const
colors = [{ id: 1, name: "blue" }, { id: 2, name: "red" }, { id: 3, name: "green" }],
shirts = [{ id: 1, color: 2 }, { id: 2, color: 3 }, { id: 3, color: 2 }, { id: 4, color: 1 }, { id: 5, color: 3 }],
colorMap = new Map(colors.map(({ id, name }) => [id, name])),
result = shirts.map(o => ({ ...o, name: colorMap.get(o.color) }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
const colors = [
{id: 1, name: "blue"},
{id: 2, name: "red"},
{id: 3, name: "green"}
];
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
shirts.forEach(s => {
s.color = (colors.find(c => c.id === s.color) || {}).name;
});
console.log(shirts);
If you use an object for the color codes, it makes your life so much easier. It avoids having to loop over it and convert it into a format that resembles this for a quick look up.
const colors = {
1: "blue",
2: "red",
3: "green"
}
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
shirts.forEach( shirt => shirt.colorName = colors[shirt.color] )
console.log(shirts)
If it really has to be in that format, than do the conversion
const colors = [
{id: 1, name: "blue"},
{id: 2, name: "red"},
{id: 3, name: "green"}
];
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
const colorCodes = colors.reduce((o, c) => { o[c.id] = c.name; return o }, {})
shirts.forEach( shirt => shirt.colorName = colorCodes[shirt.color] )
console.log(shirts)
Given
const colors = [
{id: 1, name: "blue"},
{id: 2, name: "red"},
{id: 3, name: "green"}
];
The shape of colors above is an array of objects with names and ids. This makes the code needlessly awkward.
We can dramatically simplify things by making colors an object.
const colors = {
1: "blue",
2: "red",
3: "green"
};
const shirts = [
{id: 1, color: 2},
{id: 2, color: 3},
{id: 3, color: 2},
{id: 4, color: 1},
{id: 5, color: 3}
];
function assignShirtColor(shirt) {
shirt.color = colors[shirt.color];
}
Then all we need to do is write
shirts.forEach(assignShirtColor);

Filter array of objects based on values in second array

I have an array of objects that I'd like to filter to create a new array based on whether or not the value of any key matches any value in another array.
const array1 = [{name: 'pink', id: 13}, {name: 'orange', id: 17}, {name: 'red, id: 64}, {name: 'purple', id: 47}, {name: 'yellow', id: 23}, {name: 'gray', id: 2}, {name: 'black', id: 200}, {name: 'violet', id: 4}]
const array2 = ['red', 'blue', 'green', 'pink']
I've tried using a for...of loop inside of a return function but that is giving me errors.
const array3 = array1.filter(color => {
for (mainColor of array2){
return color.name === mainColor
}
});
This works but is clearly not the best way.
const array3 = array1.filter(color => {
return (color.main === 'red') || (color.main === 'blue')
});
How can I get a third array from array1 that contains only the objects where the array1.name matches a value in array2?
Is it possible with ES6 or Lodash?
Thanks in advance!
Almost there, instead of for-loop use includes and return
const array3 = array1.filter(color => {
return array2.includes ( color.name );
});
Or
const array3 = array1.filter( color => array2.includes ( color.name ) );
Let me give an alternative, that has slightly more code, but is more efficient as well, as it only needs to scan array2 once:
const array1 = [{name: 'pink', id: 13}, {name: 'orange', id: 17}, {name: 'red', id: 64}, {name: 'purple', id: 47}, {name: 'yellow', id: 23}, {name: 'gray', id: 2}, {name: 'black', id: 200}, {name: 'violet', id: 4}],
array2 = ['red', 'blue', 'green', 'pink'];
const colorSet = new Set(array2),
array3 = array1.filter(color => colorSet.has(color.name));
console.log(array3);
Try the following with Array's includes():
const array1 = [{name: 'pink', id: 13}, {name: 'orange', id: 17}, {name: 'red', id: 64}, {name: 'purple', id: 47}, {name: 'yellow', id: 23}, {name: 'gray', id: 2}, {name: 'black', id: 200}, {name: 'violet', id: 4}]
const array2 = ['red', 'blue', 'green', 'pink'];
const array3 = array1.filter(color => array2.includes(color.name));
console.log(array3);
The filter method already iterates on each item. You just have to return true if the element is present in the second array (by using indexOf or includes)
const array1 = [{name: 'pink', id: 13}, {name: 'orange', id: 17}, {name: 'red', id: 64}, {name: 'purple', id: 47}, {name: 'yellow', id: 23}, {name: 'gray', id: 2}, {name: 'black', id: 200}, {name: 'violet', id: 4}]
const array2 = ['red', 'blue', 'green', 'pink'];
let filteredArray = array1.filter(e => array2.indexOf(e.name) > -1);
console.log(filteredArray);
I know indexOf might seem a little outfashioned and ES5-ish, but it still does the job:
const array1 = [{name: 'pink', id: 13}, {name: 'orange', id: 17}, {name: 'red', id: 64}, {name: 'purple', id: 47}, {name: 'yellow', id: 23}, {name: 'gray', id: 2}, {name: 'black', id: 200}, {name: 'violet', id: 4}],
array2 = ['red', 'blue', 'green', 'pink'];
let array3 = array1.filter(x => array2.indexOf(x.name) != -1)
console.log(array3)

Categories