0

I am designing a system and I have some bottlenecks.

I have user array such like that:

 const users = [
  {
    name: "Jack",
    workspaces: [
      {
        _id: "61216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
      {
        _id: "41ss16512315615645bk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
    ],
  },
  {
    name: "Joe",
    workspaces: [
      {
        _id: "71216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE"],
      },
    ],
  },
];

And I have activeWorkspace object such like that:

const activeWorkspace = {
  name: "W1",
  _id: "61216512315615645jbk",
};

I need to filter the objects in the users array whose workspace _id is equal to activeWorkspace _id.

Output must be like that:

{
    name: "Jack",
    workspaces: [
      {
        _id: "61216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
      {
        _id: "41ss16512315615645bk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
    ],
}

How can I do that?

In addition: If we want to return an array, not an object, how should we do it? Like that:

[{
        name: "Jack",
        workspaces: [
          {
            _id: "61216512315615645jbk",
            permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
          },
          {
            _id: "41ss16512315615645bk",
            permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
          },
        ],
}]

Thanks

Akın Şibay
  • 11
  • 1
  • 3

5 Answers5

1

If there is only one match. You need to use find(). Inside of the find method, you want to use some() to look for an _id match.

 const users = [
  {
    name: "Jack",
    workspaces: [
      {
        _id: "61216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
      {
        _id: "41ss16512315615645bk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
    ],
  },
  {
    name: "Joe",
    workspaces: [
      {
        _id: "CHANGED_ID",
        permissions: ["CAN_DELETE_WORKSPACE"],
      },
    ],
  },
];

const activeWorkspace = {
  name: "W1",
  _id: "61216512315615645jbk",
};

const active = users.find(function (user) {
  return user.workspaces.some( function (workspace) {
    return workspace._id === activeWorkspace._id;
  });
});

console.log(active);


// Same thing as above, just done with a modern approach
const active2 = users.find(({workspaces}) => workspaces.some(({_id}) => _id === activeWorkspace._id));

console.log(active2);

Now if there could be more than one match (your orginal code before the typo, you would use filter() and some() to find all users that have the workspace in their array.

 const users = [
  {
    name: "Jack",
    workspaces: [
      {
        _id: "61216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
      {
        _id: "41ss16512315615645bk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
    ],
  },
  {
    name: "Joe",
    workspaces: [
      {
        _id: "61216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE"],
      },
    ],
  },
];

const activeWorkspace = {
  name: "W1",
  _id: "61216512315615645jbk",
};

const active = users.filter(function (user) {
  return user.workspaces.some( function (workspace) {
    return workspace._id === activeWorkspace._id;
  });
});

console.log(active);


// Same thing as above, just done with a modern approach
const active2 = users.filter(({workspaces}) => workspaces.some(({_id}) => _id === activeWorkspace._id));

console.log(active2);
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • @RandyCasburn second example handles the case. – epascarello Dec 08 '21 at 14:04
  • Gotta admit, your answer is cleaner, well done, why is active2 not the modern approach anymore? – Branchverse Dec 08 '21 at 14:07
  • ah with.... lol – epascarello Dec 08 '21 at 14:10
  • Thank you so much. Both of solutions work correctly. So if we want to return an array, not an object, how should we do it? For example: [{ "name": "Jack", "workspaces": [ { "_id": "61216512315615645jbk", "permissions": [ "CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT" ] }, { "_id": "41ss16512315615645bk", "permissions": [ "CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT" ] } ] }] – Akın Şibay Dec 08 '21 at 14:13
  • The filter approach would return an array of one. It is not the best solution since it looks at everything after it finds a match. For find, you can wrap it in an array `const active = [users.find()];` – epascarello Dec 08 '21 at 14:15
0

I adjusted the provided data from Joe so he doesn't have permissions

const users = [{
    name: "Jack",
    workspaces: [{
        _id: "61216512315615645jbk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
      {
        _id: "41ss16512315615645bk",
        permissions: ["CAN_DELETE_WORKSPACE", "CAN_EDIT_PROJECT"],
      },
    ],
  },
  {
    name: "Joe",
    workspaces: [{
      _id: "61216512315615645bk",
      permissions: ["CAN_DELETE_WORKSPACE"],
    }, ],
  },
];

const activeWorkspace = {
  name: "W1",
  _id: "61216512315615645jbk",
};

function findPermittedUser() {
  return users.filter(user => {
    let hasPermission = false

    user.workspaces.forEach(workspace => {
      if (activeWorkspace._id == workspace._id) {
        hasPermission = true
      }
    })

    return hasPermission
  })
}


console.log(findPermittedUser())
Branchverse
  • 1,203
  • 1
  • 7
  • 19
  • Problem with your approach is you look at everything. You do not have to look at each index. You should use some instead of forEach so you do not do that. – epascarello Dec 08 '21 at 14:06
0

You can use map and filter to "filter" out the unwanted ids from the users object. Something like :

const users = [
    {
        "name": "Jack",
        "workspaces": [
            {
                "_id": "61216512315615645jbk",
                "permissions": [
                    "CAN_DELETE_WORKSPACE",
                    "CAN_EDIT_PROJECT"
                ]
            },
            {
                "_id": "41ss16512315615645bk",
                "permissions": [
                    "CAN_DELETE_WORKSPACE",
                    "CAN_EDIT_PROJECT"
                ]
            }
        ]
    },
    {
        "name": "Joe",
        "workspaces": [
            {
                "_id": "61216512315615645jbk",
                "permissions": [
                    "CAN_DELETE_WORKSPACE"
                ]
            }
        ]
    }
]

const activeWorkspace = {
  name: "W1",
  _id: "61216512315615645jbk",
};


const filteredUsers = users.map(item => ({ 
  name : item.name, 
  workspaces: item.workspaces.filter(user => user._id === activeWorkspace._id)}
));

console.log(filteredUsers);
Kanishk Anand
  • 1,686
  • 1
  • 7
  • 16
0

This should work (tested):

const filteredUsers = users.filter(
 user => user.workspaces.reduce(
  (acc, workspace) => acc || workspace._id === activeWorkspace._id, false)
 )
)

Explanation: We are using filter and reduce as evident from the code. What the code is doing is pretty simple, first, we want to apply filter on the user array. Now in the filter, we need to define the logic, which should return true whenever our condition happens to be true. Since we have an array of workspaces, we need to iterate over all of them to check if our activeWorkspace._id exists in any of them. For this, you can use a for loop and return true when you find it, else return false if not. But the functional way of doing it would be to use reduce and initialize the accumulator with false. Every time you access a workspace, you return acc || <our condition>. Notice how if even once our condition returns true, the accumulator becomes true for the rest of the execution of reduce. This is slightly poor in performance since you are not exiting as soon as you have found your workspace._id as you would have done in case of a for loop.

Pipe Runner
  • 82
  • 2
  • 6
-1

users.map(u => u.workspaces).flat().filter(w => w._id === activeWorkspaceId);

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

adz5A
  • 2,012
  • 9
  • 10
  • this is easy enough to put in a stack snippets and show the result. addtionally, please explain how, why this works so the others can benefit from your wisdom. If you increase the quality of your answer I'll remove my downvote. – Randy Casburn Dec 08 '21 at 13:56
  • `.map(...).flat()` can be replaced with `.flatMap(...)` – jabaa Dec 08 '21 at 13:57
  • i did not remember that this was added. i will update later when i have time if still open. interestingly enough looking for "flatten javascript" does not return great answers on google and rather show the evolution of the language https://stackoverflow.com/questions/10865025/merge-flatten-an-array-of-arrays – adz5A Dec 08 '21 at 14:00