Home > other >  Efficient method to group multiple objects by object property
Efficient method to group multiple objects by object property

Time:10-01

what is the most efficient way to group by multiple object properties? It groups by category and then by group.

I have detailed my implementation using a single array reduce which I will push into an array (expected output). I am not sure how to conditionally push to sources array which is created within the reduce.

Without using lodash groupBy or third party. I was also thinking of filtering by a key I create e.g.

const key = `${category}-${group}`;

const items = [
    {
        "name": "Halloumi",
        "group": "Cheese",
        "category": "Dairy"
    },
    {
        "name": "Mozzarella",
        "group": "Cheese",
        "category": "Dairy"
    }
];


// my current implementation
  const groupedItems = items.reduce((map, item) => {
    const { category, name, group } = item;

    if (map.has(category)) {
      map.get(category).push({
        name,
        group,
      });
    } else {
      map.set(category, [
        {
          name,
          group,
        },
      ]);
    }

    return map;
  }, new Map());

  console.log(Object.fromEntries(groupedItems));

// Another attempt
  const groupedItems2 = items.reduce((map, item) => {
    const { category, group, name } = item;

    acc[category] = acc[category] || { category, themes: [] };

    const source = {
      id,
      name
    };

    const totalSources = [source].length;
    const uniqueSources = [...new Set([source])].length;

    acc[category].themes.push({
      theme,
      totalSources,
      uniqueSources,
      sources: [source],
    });

    return acc;

    return map;
  }, {});

  console.log(Object.entries(groupedItems2));


// expected output

[
    {
      "category": "Dairy",
      "groups": [
        {
          "group": "Cheese",
          "totalSources": 2,
          "sources": [
            {
                "name": "Halloumi"
            },
            {
                "name": "Mozzarella"
            }
          ]
        }
      ]
    }
]

CodePudding user response:

You need a two-level map.

There are many ways to do that. Here is one:

const items = [{"name": "Halloumi","group": "Cheese","category": "Dairy"},{"name": "Mozzarella","group": "Cheese","category": "Dairy"}];

const dict = {};
for (const {name, group, category} of items) {
    ((dict[category] ??= {})[group] ??= []).push({name});
}
const categories = Object.entries(dict).map(([category, groups]) => ({
    category,
    groups: Object.entries(groups).map(([group, sources]) => ({
        group,
        totalSources: sources.length,
        sources
    }))
}));

console.log(categories);

  • Related