y-me
y-me

Reputation: 449

Merge duplicate items in array of objects, concating nested arrays

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

Answers (5)

greg-tumolo
greg-tumolo

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

Yevhen Horbunkov
Yevhen Horbunkov

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

Sven.hig
Sven.hig

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

EvanMorrison
EvanMorrison

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

Lino Contreras
Lino Contreras

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

Related Questions