Search code examples
javascriptecmascript-6lodashreduce

JavaScript: How to Group Array of Objects with Nested Properties?


I have this array of objects:

[
  {
    "date": "08/08/2022",
    "name": "Swordsman",
    "items": [
      {
        "item_id": 1,
        "item": {
          "item_name": "Item 1",
        },
        "count": 1
      }
    ]
  },
  {
    "date": "08/08/2022",
    "name": "Swordsman",
    "items": [
      {
        "item_id": 1,
        "item": {
          "item_name": "Item 1",
        },
        "count": 2
      },
      {
        "item_id": 2,
        "item": {
          "item_name": "Item 2",
        },
        "count": 1
      }
    ]
  }
]

What I wanted to achieve is to group by date, name and the number of items. The desired result would be:

[
  {
    "date": "08/08/2022",
    "name": "Swordsman",
    "items": [
      {
        "item_id": 1,
        "name": "Item 1",
        "count": 3
      },
      {
        "item_id": 2,
        "name": "Item 2",
        "count": 1
      }
    ]
  }
]

If it has the same date but different name like:

[
  {
    "date": "08/08/2022",
    "name": "Swordsman",
    "items": [
      {
        "item_id": 1,
        "item": {
          "item_name": "Item 1",
        },
        "count": 1
      }
    ]
  },
  {
    "date": "08/08/2022",
    "name": "Swordsman",
    "items": [
      {
        "item_id": 1,
        "item": {
          "item_name": "Item 1",
        },
        "count": 2
      },
      {
        "item_id": 2,
        "item": {
          "item_name": "Item 2",
        },
        "count": 1
      }
    ]
  },
  {
    "date": "08/08/2022",
    "name": "Archer",
    "items": [
      {
        "item_id": 1,
        "item": {
          "item_name": "Item 1",
        },
        "count": 2
      },
      {
        "item_id": 2,
        "item": {
          "item_name": "Item 2",
        },
        "count": 1
      }
    ]
  }
]

The desired result would be:

[
  {
    "date": "08/08/2022",
    "name": "Swordsman",
    "items": [
      {
        "item_id": 1,
        "name": "Item 1",
        "count": 3
      },
      {
        "item_id": 2,
        "name": "Item 2",
        "count": 1
      }
    ]
  },
  {
    "date": "08/08/2022",
    "name": "Archer",
    "items": [
      {
        "item_id": 1,
        "name": "Item 1",
        "count": 2
      },
      {
        "item_id": 2,
        "name": "Item 2",
        "count": 1
      }
    ]
  }
]

I've tried using lodash and array reduce method but still unable to get the desired result:

const groups = data.reduce((groups, data) => {
  const date = data.date;
  if (!groups[date]) {
    groups[date] = [];
  }
  
  groups[date].push(data);
  return groups;
}, {});

const groupArrays = Object.keys(groups).map((date) => {
  return {
    date,
    items: groups[date]
  };
});

How to achieve this one? Need your inputs. Thanks!


Solution

  • You can achieve this as:

    const arr = [
        {
            date: '08/08/2022',
            name: 'Swordsman',
            items: [
                {
                    item_id: 1,
                    item: {
                        item_name: 'Item 1',
                    },
                    count: 1,
                },
            ],
        },
        {
            date: '08/08/2022',
            name: 'Swordsman',
            items: [
                {
                    item_id: 1,
                    item: {
                        item_name: 'Item 1',
                    },
                    count: 2,
                },
                {
                    item_id: 2,
                    item: {
                        item_name: 'Item 2',
                    },
                    count: 1,
                },
            ],
        },
        {
            date: '08/08/2022',
            name: 'Archer',
            items: [
                {
                    item_id: 1,
                    item: {
                        item_name: 'Item 1',
                    },
                    count: 2,
                },
                {
                    item_id: 2,
                    item: {
                        item_name: 'Item 2',
                    },
                    count: 1,
                },
            ],
        },
    ];
    
    function addItemsToNewArrFromOld(oldItems, newItemsToAdd) {
        newItemsToAdd.forEach((o) => {
            const { item_id, count, item: { item_name: item }} = o;
            const itemInOldItems = oldItems.find((obj) => obj.item_id === o.item_id);
            if (itemInOldItems) itemInOldItems.count += o.count;
            else oldItems.push({ item_id, count, item });
        });
    }
    
    const map = new Map();
    arr.forEach(({ date, name, items }) => {
        const key = `${date}-${name}`;
    
        if (map.has(key)) {
            const objForKeyFound = map.get(key);
            addItemsToNewArrFromOld(objForKeyFound.items, items);
        } else {
            map.set(key, {
                date, name, items: items.map(({ item_id, item, count }) => ({ item_id, count, item: item.item_name })),
            });
        }
    });
    
    const result = [...map.values()];
    console.log(result);