Home > Software design >  Merge objects in array of object removing duplicates
Merge objects in array of object removing duplicates

Time:06-23

I have this array of objects:

const a = [
   {
      id: 1,
      name: 'John',
      role: 'admin'
   },
   {
      id: 1,
      name: 'John',
      role: 'user'
   },
   {
      id: 2,
      name: 'Max',
      role: 'user'
   }  
]

I would like to have a result like this, so having one object for id:1 and a merged array in role property:

const a = [
   {
      id: 1,
      name: 'John',
      role: ['admin', 'user']
   },
   {
      id: 2,
      name: 'Max',
      role: 'user'
   }  
]

EDIT: I am able to remove duplicates when I have just to properties in the object. In my case I don't know how to retrieve the name property using the following snippet:

const b = [...new Set(a.map(d => d.id))].map(obj => {
  return {
    id: obj,
    data: a.filter(d => d.id === obj).map(d => d.role)
  }
})

CodePudding user response:

give this a try

const a = [
   {
      id: 1,
      name: 'John',
      role: 'admin'
   },
   {
      id: 1,
      name: 'John',
      role: 'user'
   },
   {
      id: 2,
      name: 'Max',
      role: 'user'
   }
]

const newArr = a.reduce((acc, val) => {
    const findIndex = acc.findIndex(f => f.id === val.id);
    if (findIndex > -1) {
        if ((typeof acc[findIndex].role === 'string')) {
            acc[findIndex].role =  [acc[findIndex].role, val.role]
        } else {
            acc[findIndex].role.push(val.role)
        }
        
    } else {
        acc.push(val)
    }
    return acc
}, []);

console.log(newArr)

CodePudding user response:

It can be done very simply with a reducer:

const a = [
   {
      id: 1,
      name: 'John',
      role: 'admin'
   },
   {
      id: 1,
      name: 'John',
      role: 'user'
   },
   {
      id: 2,
      name: 'Max',
      role: 'user'
   }  
]

const b = a.reduce((acc, el)=>{
  const existingEl = acc.find(accEl=>accEl.id === el.id)
  if(existingEl) existingEl.role.push(el.role)
  // a very inelegant way of building a shallow copy with
  // a bit of a data structure change
  else acc.push({id: el.id, name: el.name, role:[el.role]})
  return acc
}, [])

console.log(b)

CodePudding user response:

You could take an object for grouping and use an array for additional roles.

const
    data = [{ id: 1, name: 'John', role: 'admin' }, { id: 1, name: 'John', role: 'user' }, { id: 2, name: 'Max', role: 'user' }],
    result = Object.values(data.reduce((r, o) => {
        if (!r[o.id]) r[o.id] = { ...o };
        else r[o.id].role = [].concat(r[o.id].role, o.role);
        return r;
    }, {}));
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

CodePudding user response:

For problems like this I usually turn the array into an object dictionary to merge all the duplicates, then convert the dictionary back to an array:

const a = [{
    id: 1,
    name: 'John',
    role: 'admin'
  },
  {
    id: 1,
    name: 'John',
    role: 'user'
  },
  {
    id: 2,
    name: 'Max',
    role: 'user'
  }
];

// Merge duplicates using object dictionary.
let itemsById = {};
for (let item of a) {
  if (!itemsById[item.id]) {
    // Id not seen yet.
    item.role = [item.role];
    itemsById[item.id] = item;
  } else {
    // Duplicate Id.
    itemsById[item.id].role.push(item.role);
  }
}

// Convert object dictionary back to array.
let newArray = [];
for (const id in itemsById) {
  let item = itemsById[id];
  if (item.role.length == 1) {
    item.role = item.role[0];
  }
  newArray.push(item);
}

console.log(newArray);

CodePudding user response:

You can iterate over each item in your input, storing its data on an object keyed by the item's id property. Using a Set to collect the roles during iteration ensures that no duplicates will exist in the end result:

function mergeRoles (users) {
  const merged = {};

  for (const {id, name, role} of users) {
    (merged[id] ??= {id, name, role: new Set([role])}).role.add(role);
  }

  return Object.values(merged).map(user => ({...user, role: [...user.role]}));
}

const input = [
  { id: 1, name: 'John', role: 'admin' },
  { id: 1, name: 'John', role: 'user'  },
  { id: 2, name: 'Max',  role: 'user'  },
];

const result = mergeRoles(input);
console.log(result);

  • Related