Home > Blockchain >  Best way to merge and transform keys and arrays from an object
Best way to merge and transform keys and arrays from an object

Time:06-11

I have an object that looks like this:

{ role1: ["id1", "id2", "id3"], role2: ["id1", "id2"], role3: ["id2", "id4"] }

I would like to transform this into an array of objects, making the ids the unique identifiers and the roles into arrays, like so:

[
  { id: "id1", roles: ["role1", "role2"] },
  { id: "id2", roles: ["role1", "role2", "role3"] },
  { id: "id3", roles: ["role1"] },
  { id: "id4", roles: ["role3"] },
]

This is how I do it right now, but I'm not sure if there's a better way. It feels like I'm overcomplicating things.

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"]
};
const users = [];

Object.entries(obj).forEach(([key, ids]) => {
  ids.forEach(id => {
    const user = users.find(x => x.id === id);
    if (user) {
      user.roles.push(key);
      return;
    }
    users.push({
      id,
      roles: [key]
    });
  });
});

console.log(users);

CodePudding user response:

You could probably just transform it into an object like so:

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"]
};
let userDict = {};

Object.entries(obj).forEach(([key, ids]) => {
  ids.forEach(id => {
    userDict[id] = (userDict[id] || []);
    userDict[id].push(key);
  });
});

let users = Object.entries(userDict).map(([id, roles]) => ({ id, roles }));

console.log(users);
.as-console-wrapper { max-height: 100% !important; top: auto; }

Or even use reduce like so:

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"]
};
let userDict = Object.entries(obj).reduce((acc, [role, ids]) => {
  ids.forEach(id => {
    acc[id] = (acc[id] || []);
    acc[id].push(role);
  });
  return acc;
}, {});

let users = Object.entries(userDict).map(([id, roles]) => ({ id, roles }));

console.log(users);
.as-console-wrapper { max-height: 100% !important; top: auto; }

Either option is more concise and simple than using an array all the way through.

CodePudding user response:

just another version with Lodash library

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"]
};

const users = _.chain(obj)
  .entries()
  .reduce((accm, [role, ids]) => {
    ids.forEach((id) => (accm[id] || (accm[id] = [])).push(role));
    return accm;
  }, {})
  .entries()
  .map(([id, roles]) => ({ id, roles }));

CodePudding user response:

The First thing I get all id from that object by using Set (to avoid duplicates) with flat

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"],
};
const objsId = [
  ...new Set(
    Object.values(obj) //[["id1","id2","id3"],["id1","id2"],["id2","id4"]]
      .flat() //['id1', 'id2', 'id3', 'id1', 'id2', 'id2', 'id4']
  ),
]; 
console.log(objsId); //['id1', 'id2', 'id3', 'id4']

The second thing is creating an object from that array by using Object.fromEntries

Method transforms a list of key-value pairs into an object.

const objsId = ['id1', 'id2', 'id3', 'id4'];
const idRoles = Object.fromEntries(
  objsId.map((id) => [id, []]) //[["id1",[]],["id2",[]],["id3",[]],["id4",[]]]
); 
console.log(idRoles) //{"id1":[],"id2":[],"id3":[],"id4":[]}

Finally loop over our array objsId and for each id, loop over your object obj entries using Object.fromEntries() with for..of And if the current id exists in roles, So add affect that role into idRoles

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"],
};
const objsId = ['id1', 'id2', 'id3', 'id4'];
const idRoles = { id1: [], id2: [], id3: [], id4: [] };
for (const id of objsId) {
  for (const [role, ids] of Object.entries(obj)) {
    if (ids.includes(id)) idRoles[id].push(role);
  }
}
console.log(idRoles);

const obj = {
  role1: ["id1", "id2", "id3"],
  role2: ["id1", "id2"],
  role3: ["id2", "id4"],
};
const objsId = [
  ...new Set(
    Object.values(obj) //[["id1","id2","id3"],["id1","id2"],["id2","id4"]]
      .flat() //['id1', 'id2', 'id3', 'id1', 'id2', 'id2', 'id4']
  ),
]; //['id1', 'id2', 'id3', 'id4']

const idRoles = Object.fromEntries(
  objsId.map((id) => [id, []]) //[["id1",[]],["id2",[]],["id3",[]],["id4",[]]]
); //{"id1":[],"id2":[],"id3":[],"id4":[]}

for (const id of objsId) {
  for (const [role, ids] of Object.entries(obj)) {
    if (ids.includes(id)) idRoles[id].push(role);
  }
}
const result = [];
for (let [id, roles] of Object.entries(idRoles)) {
  result.push({ id, roles });
}
console.log(JSON.stringify(result, null, 4));

  • Related