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