proph3t
proph3t

Reputation: 965

Map Nested Object Key To Another Key

Working with the following object, how can the roles => name be added to the sites => userPermission object with the matching roleId?

For example, the userPermission key of the first sites entry would be updated to:

 "userPermission": {
   "roleId": 6,
   "roleName": "Field Representative"
 }

Once the roleName key has been mapped, there is no longer a need for the roles array and it can be removed from the end result as shown in the expected outcome.

const obj = {
  "id": 542,
  "createdAt": "2018-12-06T22:34:12.553Z",
  "sites": [
    {
      "id": 10,
      "siteId": "sixtysixone",
      "edition": "pro",
      "userPermission": {
        "roleId": 6
      }
    },
    {
      "id": 2,
      "siteId": "amplify",
      "edition": "pro",
      "userPermission": {
        "roleId": 4
      }
    }
  ],
  "roles": [
    {
      "id": 6,
      "name": "Field Representative"
    },
    {
      "id": 4,
      "name": "Program Manager"
    }
  ]
};

Expected outcome:

const outcome = {
  "id": 542,
  "createdAt": "2018-12-06T22:34:12.553Z",
  "sites": [
    {
      "id": 10,
      "siteId": "sixtysixone",
      "edition": "pro",
      "userPermission": {
        "roleId": 6,
        "roleName": "Field Representative"
      }
    },
    {
      "id": 2,
      "siteId": "amplify",
      "edition": "pro",
      "userPermission": {
        "roleId": 4
        "roleName": "Program Manager"
      }
    }
  ]
};

I have attempted with a combination of .map and .find, but feel there is a much more simple/readable way to accomplish this.

const outcome = obj.map(o => ({
  ...o,
  sites: o.sites
    .map(s => ({
      ...s,
      roleName: o.roles
        .find(r => r.id === s.roleId).name,
   })),
}));

Upvotes: 2

Views: 1366

Answers (3)

ibrahim mahrir
ibrahim mahrir

Reputation: 31712

You could first turn the roles array into an object to ease the retrieval of the role names:

let rolesMap = obj.roles.reduce((acc, role) =>
    (acc[role.id] = role.name, acc),
    Object.create(null)
);

Then just loop over the sites array, adding the roleName property to each site userPermission object by fetching the value from rolesMap:

obj.sites.forEach(site => {
    if(rolesMap[site.userPermission.roleId]) {
        site.userPermission.roleName = rolesMap[site.userPermission.roleId];
    }
});

You can skip the if test if you know for sure that each site object will have an associated role object in the roles array. And if you want to create a new sites object then use map instead of forEach.

And finally you can delete the roles property if you want:

delete obj.roles;

Example:

const obj = {"id":542,"createdAt":"2018-12-06T22:34:12.553Z","sites":[{"id":10,"siteId":"sixtysixone","edition":"pro","userPermission":{"roleId":6}},{"id":2,"siteId":"amplify","edition":"pro","userPermission":{"roleId":4}}],"roles":[{"id":6,"name":"Field Representative"},{"id":4,"name":"Program Manager"}]};

let rolesMap = obj.roles.reduce((acc, role) =>
    (acc[role.id] = role.name, acc),
    Object.create(null)
);

obj.sites.forEach(site => {
    if(rolesMap[site.userPermission.roleId]) {
        site.userPermission.roleName = rolesMap[site.userPermission.roleId];
    }
});

delete obj.roles;

console.log(obj);

Upvotes: 1

Jonas Wilms
Jonas Wilms

Reputation: 138537

Create a map of roles:

 const roles = new Map(obj.roles.map(({ id, name }) => [id, { roleId: id, roleName: name }]));

Then you can just look up:

const outcome = {
  ...obj,
  sites: obj.sites.map(site => ({ 
    ...site,
    userPermission: roles.get(site.userPermission.roleId),
  }),
  roles: undefined,
};

Upvotes: 3

adiga
adiga

Reputation: 35263

You could loop over the sites and update the userPermission using find

(This assumes that every roleId exists in roles. Otherwise, you need to check if find returns undefined first)

const obj = {"id":542,"createdAt":"2018-12-06T22:34:12.553Z","sites":[{"id":10,"siteId":"sixtysixone","edition":"pro","userPermission":{"roleId":6}},{"id":2,"siteId":"amplify","edition":"pro","userPermission":{"roleId":4}}],"roles":[{"id":6,"name":"Field Representative"},{"id":4,"name":"Program Manager"}]};

obj.sites.forEach(site => {
  site.userPermission.roleName = 
        obj.roles.find(r => r.id === site.userPermission.roleId).name
})

delete obj.roles;
console.log(obj)

Upvotes: 1

Related Questions