Reputation: 449
I have an array of objects that contains duplicates. I need to remove the duplicate while keeping the nested items objects.
To make it clear, this is what my source array looks like:
const src = [
{
id: '5f10b85e145d7163d818066e',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '5f10b8f6145d7163d8180672',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
],
},
{
id: '5f10b856145d7163d818066d',
name: 'Product 1',
vendor: 'Vendor 2',
instances: [
{
id: '5f10b8c5145d7163d8180671',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
},
],
},
{
id: '5f10b85e145d7163d818066e',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '5f10b8f6145d7163d8180672',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
],
},
];
Product 1
of Vendor 1
(with the same id
) is duplicated.
I need the instances of the duplicates, to be merged with the first item.
The result array should look like this:
const target = [
{
id: '5f10b85e145d7163d818066e',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [
{
id: '5f10b8f6145d7163d8180672',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
{
id: '5f10b8f6145d7163d8180672',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
},
],
},
{
id: '5f10b856145d7163d818066d',
name: 'Product 1',
vendor: 'Vendor 2',
instances: [
{
id: '5f10b8c5145d7163d8180671',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
},
],
},
];
Upvotes: 0
Views: 124
Reputation: 696
Use a map from id
to objects like:
const map = {}
...
// Given object:
if (object.id in map) {
const object_master = map[object.id]
object_master.instances.push(...object.instances)
} else map[object.id] = object
Upvotes: 0
Reputation: 15540
You may use Array.prototype.reduce()
to traverse source array and build up the Map
where id
is used as a key, so once you get duplicating id
, you push instances of the current object into existing entry, or set new entry, otherwise. When Map
is ready, you simply extract its values with Map.prototype.values()
:
const src = [{id:'5f10b85e145d7163d818066e',name:'Product 1',vendor:'Vendor 1',instances:[{id:'5f10b8f6145d7163d8180672',operatingSystem:'Microsoft Windows 2016',environment:'Prod',version:'4.0',notes:'',},],},{id:'5f10b856145d7163d818066d',name:'Product 1',vendor:'Vendor 2',instances:[{id:'5f10b8c5145d7163d8180671',operatingSystem:'Microsoft Windows 2012R2',environment:'Prod',version:'4.1',notes:'',},],},{id:'5f10b85e145d7163d818066e',name:'Product 1',vendor:'Vendor 1',instances:[{id:'5f10b8f6145d7163d8180672',operatingSystem:'Microsoft Windows 2016',environment:'Prod',version:'4.0',notes:'',},],},],
result = [...src
.reduce((r, o) => {
const dupe = r.get(o.id)
dupe ?
dupe.instances.push(...o.instances) :
r.set(o.id, o)
return r
}, new Map())
.values()
]
console.log(result)
.as-console-wrapper{min-height:100%;}
Upvotes: 1
Reputation: 4519
You could use reduce
and then wrap the response with Object.values
to remove the keys added by reduce
const src = [ { id: '5f10b85e145d7163d818066e', name: 'Product 1', vendor: 'Vendor 1', instances: [ { id: '5f10b8f6145d7163d8180672', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', }, ], }, { id: '5f10b856145d7163d818066d', name: 'Product 1', vendor: 'Vendor 2', instances: [ { id: '5f10b8c5145d7163d8180671', operatingSystem: 'Microsoft Windows 2012R2', environment: 'Prod', version: '4.1', notes: '', }, ], }, { id: '5f10b85e145d7163d818066e', name: 'Product 1', vendor: 'Vendor 1', instances: [ { id: '5f10b8f6145d7163d8180672', operatingSystem: 'Microsoft Windows 2016', environment: 'Prod', version: '4.0', notes: '', }, ], }, ];
var res=src.reduce((a,c)=>{
if (!a[c.id]) a[c.id]= {...a[c.id],...c}
else {
a[c.id].instances.push(c.instances)
a[c.id].instances=[].concat(...a[c.id].instances);
}
return a
},{})
console.log(Object.values(res))
Upvotes: 1
Reputation: 1217
There's a number of ways to go about it. Here's one.
const src = [{
id: '5f10b85e145d7163d818066e',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [{
id: '5f10b8f6145d7163d8180672',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
}, ],
},
{
id: '5f10b856145d7163d818066d',
name: 'Product 1',
vendor: 'Vendor 2',
instances: [{
id: '5f10b8c5145d7163d8180671',
operatingSystem: 'Microsoft Windows 2012R2',
environment: 'Prod',
version: '4.1',
notes: '',
}, ],
},
{
id: '5f10b85e145d7163d818066e',
name: 'Product 1',
vendor: 'Vendor 1',
instances: [{
id: '5f10b8f6145d7163d8180672',
operatingSystem: 'Microsoft Windows 2016',
environment: 'Prod',
version: '4.0',
notes: '',
}, ],
},
];
function deDupe(input) {
let result = [];
let productMap = {};
input.forEach((product, idx) => {
if (!productMap[product.id]) {
productMap[product.id] = input[idx];
} else {
productMap[product.id].instances.push(...input[idx].instances);
}
});
return Object.values(productMap)
}
console.log(deDupe(src));
Upvotes: 1
Reputation: 89
You could use a Map to keep the ids and the first occurrence of an object.
Then using the filter function check if an id has already been added.
const src = [
{ id: 'foo', name: 'Product 1', instances: [{ key: 'value a' }] },
{ id: 'bar', name: 'Product 2', instances: [{ key: 'value b' }] },
{ id: 'foo', name: 'Product 1', instances: [{ key: 'value c' }] }
];
const unique = new Map();
const target = src.filter((product) => {
if (unique.has(product.id)) {
unique.get(product.id).instances.push(...product.instances);
return false;
}
unique.set(product.id, product);
return true;
});
console.log(target);
Upvotes: 1