简体   繁体   中英

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!

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);

  • Using Array#reduce , iterate over the list while updating a Map where the key is <date>-<name> and the value is the grouped object (having all items of this key).
  • Using Array#map , iterate over the above grouped objects. For each object, we need to group its items by item_id while updating the count .

 const groupByDateAndName = arr => [... arr.reduce((map, { date, name, items = [] }) => { const key = `${date}-${name}`; const { items: prevItems = [] } = map.get(key)?? {}; map.set( key, { date, name, items: [...prevItems, ...items.map(({ item_id, item, count }) => ({ item_id, count, name: item.item_name })) ] } ); return map; }, new Map).values() ]; const groupItemsById = arr => arr.map(e => { const items = [... e.items.reduce((map, { item_id, name, count }) => { const { count: prevCount = 0 } = map.get(item_id)?? {}; map.set(item_id, { item_id, name, count: prevCount + count }) return map; }, new Map).values() ] return {...e, items }; }); const group = (arr = []) => { const list = groupByDateAndName(arr); return groupItemsById(list); } 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 } ] } ]; console.log( group(arr) );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM