Amine Da.
Amine Da.

Reputation: 1164

Group objects by multiple properties including nested properties and sump up their values

it's like Group objects by multiple properties in array then sum up their values but with nested elements and more complicated. I've been struggling for hours.

I have an array of products:

a product looks like this :

{
    "commissioningDate": "2019-09-27",
    "_product": {
        "_id": "aaa",
        "name": "Installation"
    },
    "zones": [
        {
            "_zone": {
                "_id": "KK",
                "name": "Zone kk"
            },
            "category": "category1",
            "zone_quantity": 6
        }
    ],
    "product_quantity": 3
}

Expected behavior

I made this gist because the example is too long.

Problem

So I have an array of products.

1) products in this array are considered duplicates only if both their commissioningDate and _product._id are the same

2) if many products gets merged into a single product:

3) zones of a merged product in this array are considered duplicates only if both their _zone._id and category are the same

4) if many zones gets merged into a single zone we need a to sum-up the zone_quantity

Upvotes: 1

Views: 114

Answers (1)

Colin
Colin

Reputation: 1215

Assume your single product zones always has length 1.

const sample = [
    {
        "commissioningDate": "2019-09-27",
        "_product": {
            "_id": "aaa",
            "name": "Installation"
        },
        "zones": [
            {
                "_zone": {
                    "_id": "KK",
                    "name": "Zone kk"
                },
                "category": "category1",
                "zone_quantity": 6
            }
        ],
        "product_quantity": 3
    },
    {
        "commissioningDate": "2019-09-27",
        "_product": {
            "_id": "aaa",
            "name": "Installation"
        },
        "zones": [
            {
                "_zone": {
                    "_id": "KK",
                    "name": "Zone kk"
                },
                "category": "category2",
                "zone_quantity": 3
            }
        ],
        "product_quantity": 2
    },
    {
        "commissioningDate": "2019-09-27",
        "_product": {
            "_id": "aaa",
            "name": "Installation"
        },
        "zones": [
            {
                "_zone": {
                    "_id": "KK",
                    "name": "Zone kk"
                },
                "category": "category2",
                "zone_quantity": 4
            }
        ],
        "product_quantity": 5
    },
    {
        "commissioningDate": "2019-09-27",
        "_product": {
            "_id": "aaa",
            "name": "Installation"
        },
        "zones": [
            {
                "_zone": {
                    "_id": "CC",
                    "name": "Zone cc"
                },
                "category": "category2",
                "zone_quantity": 6
            }
        ],
        "product_quantity": 1
    },
    {
        "commissioningDate": "2019-09-27",
        "_product": {
            "_id": "bbbb",
            "name": "Installation"
        },
        "zones": [
            {
                "_zone": {
                    "_id": "CC",
                    "name": "Zone cc"
                },
                "category": "category2",
                "zone_quantity": 8
            }
        ],
        "product_quantity": 2
    },
    {
        "commissioningDate": "2019-09-26",
        "_product": {
            "_id": "bbbb",
            "name": "Installation"
        },
        "zones": [
            {
                "_zone": {
                    "_id": "CC",
                    "name": "Zone cc"
                },
                "category": "category2",
                "zone_quantity": 8
            }
        ],
        "product_quantity": 2
    }
]

//reduce initialze value is an empty object
const res = sample.reduce((group, item) => {
  //for each item, generate a key k by combining item commissioningDate and item _product._id seperated //by a comma 
  const k = `${item.commissioningDate},${item._product._id}`;
  
  //check if this key k exists in our object group(which is an empty object when we check the first //item)
  //if it is not in the object, we save the key k and its value which is current item into the object //group
  if(!group[k]) group[k] = Object.assign({}, item);
  
  //if it is in the object already 
  else{
  
  //we sum up current item quantity to the group of this item
    group[k].product_quantity+=item.product_quantity;
    
    //find index of zone in current group zones has the same zone id and category as item's
    for(const itemZone of item.zones){
      const zoneIdx = group[k].zones.findIndex(zone => zone._zone._id === itemZone._zone._id && zone.category === itemZone.category)

      //index is -1, it's not in group zones, we push the zone to group zones array
      if(zoneIdx === -1){
        group[k].zones.push(itemZone)
      }
      //in group zones, we sum up zone_quantity
      else{
        group[k].zones[zoneIdx].zone_quantity += itemZone.zone_quantity
      }
    }
    
  }
  //update current group
  return group
}, {})

//recall keys are our custom identifier for different groups of items, values are actually groups of //items, so we only need to get values from group object
console.log(Object.values(res))

Upvotes: 2

Related Questions