0

Given a tree-shaped object:

  • a User has many Roles.
  • a Role has many PermissionCollections
  • a PermissionCollection has many Permissions

How do you flatten that with javascript to end up with an array of permissions

Something like:

user.roles.map(r = r.permissionGroups).flatten().reduce(pg => pg.permissions).flatten();

Is that a classic map reduce problem?

2 Answers 2

3

Here's a (shallow) flatten function:

var flatten = array => array.reduce( ( a, b ) => a.concat( b ), [] );

You can apply it like this.

var permissionGroups = flatten( user.roles.map( r => r.permissionGroups ) );
var permissions = flatten( permissionGroups.map( pg => pg.permissions ) );
Sign up to request clarification or add additional context in comments.

1 Comment

ah right that is nice and easy to read. and underscore already has a flatten, so guessing that does the same thing...will try it out now.
3

You're correct that reduce() will probably give the cleanest approach.

You can concat() together the arrays:

const allPermissions = user.roles.reduce((r, role) => 
  r.concat(role.permissionGroups.reduce((r, group) => 
    r.concat(group.permission), []), 
  []);

Note that will actually create a new array every single time it returns, so if there are lots of roles, it could be a bit inefficient. To do it without creating a new array every time, you could use push.apply() instead:

const allPermissions = user.roles.reduce((r, role) => 
  r.push.apply(r, role.permissisionGroups.reduce((r, group) => 
    r.push.apply(r, group.permissions), []), 
  []);

Using apply() will let you pass in the permissions as the arguments (basically the same as calling push() a lot). If you try to push an array, it'll push it as an array instead of the individual values.

If you want to take it a step further and keep one array the whole time, you can use:

const allPermissions = []; user.roles.forEach(role => role.permissionGroups.forEach(group => allPermissions.push.apply(allPermissions, group.permissions) ) )

This is still basically a reduce() pattern, but doesn't create lots of extra values. If you wanted to use reduce() with it, it could look like this:

const allPermissions = user.roles.reduce((r, role) =>
  r.push.apply(r, role.permissionGroups.reduce((r, group) =>
    r.push.apply(r, group.permissions)), r), 
  []
);

1 Comment

great answer. thanks for the explanation. i think i prefer ben west's answer purely based on its readability - although i hear what you say re: inefficiency. however we are talking 10s of items in each array...

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.