1

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)
  }
})
2
  • 1
    I suggest looing at Array.reduce() method and letting the community know what you tried. re: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Jun 22, 2022 at 13:31
  • 3
    You also probably want an array for role no matter how many elements are in it. Commented Jun 22, 2022 at 13:33

5 Answers 5

3

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

Sign up to request clarification or add additional context in comments.

Comments

2

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)

1 Comment

I like this approach because it can be chained on my precious fetched and mapped array.
1

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)

Comments

1

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

2 Comments

What does "??=" means?
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.