Home > Back-end >  Group Array of objects into 2D Array by the property of an object contained into an array
Group Array of objects into 2D Array by the property of an object contained into an array

Time:12-07

I have an array of objects like this:

[
  {
    "id": 1,
    "name": "Question1",
    "categories": [
      {
        "id": 20,
        "name": "Science"
      },
      {
        "id": 21,
        "name": "General Knowledge"
      },
      {
        "id": 20,
        "name": "Mathematic"
      }
    ]
  },
  {
    "id": 2,
    "name": "Question2",
    "categories": [
      {
        "id": 20,
        "name": "Science"
      }
    ]
  },
  {
    "id": 3,
    "name": "Question3",
    "categories": [
      {
        "id": 20,
        "name": "Science"
      }
    ]
  }
]

and I want to group the objects into sub-arrays (Sorted alphabetically) by the object property name of the array categories.

The expected output would be:

[
  //General Knowledge
  [
    {
      "id": 1,
      "name": "Question1",
      "categories": [
        {
          "id": 20,
          "name": "Science"
        },
        {
          "id": 21,
          "name": "General Knowledge"
        },
        {
          "id": 20,
          "name": "Mathematic"
        }
      ]
    }
  ],
  //Mathematic
  [
    {
      "id": 1,
      "name": "Question1",
      "categories": [
        {
          "id": 20,
          "name": "Science"
        },
        {
          "id": 21,
          "name": "General Knowledge"
        },
        {
          "id": 20,
          "name": "Mathematic"
        }
      ]
    }
  ],
  //Science
  [
    {
      "id": 1,
      "name": "Question1",
      "categories": [
        {
          "id": 20,
          "name": "Science"
        },
        {
          "id": 21,
          "name": "General Knowledge"
        },
        {
          "id": 20,
          "name": "Mathematic"
        }
      ]
    },
    {
      "id": 2,
      "name": "Question2",
      "categories": [
        {
          "id": 20,
          "name": "Science"
        }
      ]
    },
    {
      "id": 3,
      "name": "Question3",
      "categories": [
        {
          "id": 20,
          "name": "Science"
        }
      ]
    }
  ]
]

I've been trying using loops and Array.reduce(), but I didn't manage to get it working.

Which one would be the most performant way to achieve the expected output? Many thanks!

CodePudding user response:

Loop through each object and inside that loop through the categories. Use each category name as the key and an array of questions as value in the group object

const group = {}

for (const q of questions) {
  for(const c of q.categories) {
    group[c.name] ??= []
    group[c.name].push(q)
  }
}

const output = Object.values(group)

Here's a snippet:

const questions =[{id:1,name:"Question1",categories:[{id:20,name:"Science"},{id:21,name:"General Knowledge"},{id:20,name:"Mathematic"}]},{id:2,name:"Question2",categories:[{id:20,name:"Science"}]},{id:3,name:"Question3",categories:[{id:20,name:"Science"}]}];

const group = {}

for (const q of questions) {
  for(const c of q.categories) {
    group[c.name] ??= []
    group[c.name].push(q)
  }
}

const output = Object.values(group)

document.querySelector("pre").innerText = JSON.stringify(output, null, 4)
<pre />

CodePudding user response:

Assuming your expected output is correct, this is what I've done. I don't know that this is the most performant way to do it, but it works:

const input = [
  {
    id: 1,
    name: "Question1",
    categories: [
      {
        id: 20,
        name: "Science",
      },
      {
        id: 21,
        name: "General Knowledge",
      },
      {
        id: 20,
        name: "Mathematic",
      },
    ],
  },
  {
    id: 2,
    name: "Question2",
    categories: [
      {
        id: 20,
        name: "Science",
      },
    ],
  },
  {
    id: 3,
    name: "Question3",
    categories: [
      {
        id: 20,
        name: "Science",
      },
    ],
  },
];

// First we put all subjects into an array.
const subjects = [];
for (const element of input) {
  for (const categories of element.categories) {
    if (!subjects.find((e) => e === categories.name)) {
      subjects.push(categories.name);
    }
  }
}

// then we add the element to our output ordered by subjet
const output = [];
for (const subject of subjects) {
  const tempOutput = [];
  for (const element of input) {
    if (element.categories?.find((e) => e.name === subject)) {
      tempOutput.push(element);
    }
  }
  output.push(tempOutput);
}

console.log(output);

  • Related