1

How would you group an array by a property of the objects in a nested array. Either vanilla or lodash.

For example, how would you group an array of tasks by the assignee, when tasks can have more than one person assigned.

Example data:

[
  {
    "assignees": [
      {
        "name": "John",
        "uid": "id0001"
      },
      {
        "name": "Sally",
        "uid": "id0002"
      }
    ],
    "title": "Task 1",
    "status": "To do"
  },
  {
    "assignees": [
      {
        "name": "John",
        "uid": "id0001"
      }
    ],
    "title": "Task 2",
    "status": "To do"
  },
  {
    "assignees": [
      {
        "name": "Sally",
        "uid": "id0002"
      }
    ],
    "title": "Task 3",
    "status": "To do"
  }
]

Example desired result:


  {
    "id0001": {
      "name": "John",
      "tasks": [
        {
          "title": "Task 1",
          "status": "To do"
        },
        {
          "title": "Task 2",
          "status": "To do"
        }
      ]
    }
  },
  {
    "id0002": {
      "name": "Sally",
      "tasks": [
        {
          "title": "Task 1",
          "status": "To do"
        },
        {
          "title": "Task 3",
          "status": "To do"
        }
      ]
    }
  }

I have a lot of things both vanilla and lodash - a simple example is below. I was expecting an array of three groups. One for Sally, one for John and one for Sally and John:

  const grouped = _.groupBy(taskList, (task) => task.assignees);

But that returns one group [object Object] with all the tasks in it.

ReactSwift
  • 13
  • 3
  • The desired output is odd. I would rather expect a simpler output like `{id0001: {}, id0002: {}}`. The array is useless here. IDs are unique anyways. And it's easier to find the tasks for a specific user ID - directly, than having to iterate over an Array. – Roko C. Buljan Dec 16 '22 at 23:54
  • In general, if you have an array of objects they should all have the same property names. If you want `idXXX` to be the property name, they should all be in a single property. – Barmar Dec 16 '22 at 23:56
  • 1
    You need nested loops. The outer loop iterates over the array of tasks, the inner loop iterates over the assignees array. In the inner loop you create elements of the result object that are keyed by assignee ID. – Barmar Dec 16 '22 at 23:57
  • 1
    "I was expecting an array of three groups." That's not what your example desired result shows... To get what is shown, you should be able to follow [javascript group a nested array of objects by child object value](https://stackoverflow.com/q/71963524/215552) and [Grouping Arrays By Nested Arrays](https://stackoverflow.com/q/49507221/215552) – Heretic Monkey Dec 16 '22 at 23:57
  • 1
    @RokoC.Buljan yes that makes sense sorry, I was trying to be helpful with a desired result but didn’t take much care – ReactSwift Dec 17 '22 at 00:02

1 Answers1

0

Since uids are unique, the Array seems like an overhead. I suggest to get an Object literal with the tasks grouped by the user's uids: like:

{
  id0001: {...user, tasks: []},
  id0002: {...user, tasks: []},
}

Example:

const groupTasksByUserId = (arr) => arr.reduce((ob, {assignees, ...task}) => {
  assignees?.forEach((user) => {
    ob[user.uid] ??= {...user, tasks: []};
    ob[user.uid].tasks.push(task)
  });
  return ob;
}, {});

const data = [{
  "assignees": [{"name": "John", "uid": "id0001"}, {"name": "Sally","uid": "id0002"}],
  "title": "Task 1",
  "status": "To do"
}, {
  "assignees": [{"name": "John", "uid": "id0001"}],
  "title": "Task 2",
  "status": "To do"
}, {
  "assignees": [{"name": "Sally", "uid": "id0002"}],
  "title": "Task 3",
  "status": "To do"
}];

const res = groupTasksByUserId(data);
// console.log(res);
console.log(JSON.stringify(res, 0, 2));

Find out more about the above used Array methods (reduce and forEach) on MDN Docs

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313