-5

I am trying to create an object that will be filled with values from an existing object. In my case it should have as result per "Material" an object list with the "Code" and the "Quantity". In addition, if the "Material" and "Code" are the same, the "Quantity" should be summed up. I hope that you can see from my example what I mean. Thanks

const arr = [{
  "Material": "123",
  "Code": "AAA",
  "Quantity": 1
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "456",
  "Code": "CCC",
  "Quantity": 7
}]

var arrResult = [{
  "Material": "123",
  "CodeQuantity": [{
    "Code": "AAA",
    "Quantity": 1
  }, {
    "Code": "BBB",
    "Quantity": 4
  }]
}, {
  "Material": "456",
  "CodeQuantity": [{
    "Code": "CCC",
    "Quantity": 7
  }]
}]

console.log(arr)
console.log("Result:", arrResult)
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
InFlames82
  • 493
  • 6
  • 17
  • Does this answer your question? [How can I group an array of objects by key?](https://stackoverflow.com/questions/40774697/how-can-i-group-an-array-of-objects-by-key) - it may not be exact, but it should help you get 80% there – evolutionxbox Feb 15 '22 at 13:18
  • The questions is how to sum the values if "Material" and "Code" matches? – InFlames82 Feb 15 '22 at 13:22
  • The question I linked to has all sorts of suggestions about how to group the values. https://stackoverflow.com/questions/1230233/how-to-find-the-sum-of-an-array-of-numbers shows how to sum them – evolutionxbox Feb 15 '22 at 13:30

3 Answers3

0

An approach should consider breaking the OP's entire task into two separate ones.

Within an intermediate step one firstly does reduce the list of material items into a map of (grouped) material items.

The final step does a map-reduce on the values of the intermediate result, where the reduce task is responsible for summing up a material's Code specific Quantity value ...

function collectTotalCodeQuantity(index, item) {
  const { Code, Quantity } = item;

  // access or create the `Code` specific code quantity item.
  const codeQuantityItem = (index[Code] ??= { Code, Quantity: 0 });
  // sum up a `Code` specific code quantity item's quantity value.
  codeQuantityItem.Quantity += Quantity;

  return index;
}
function createMaterialGroup(index, item) {
  const { Material: materialValue, ...rest } = item;
  // create the `CodeQuantity` key in a more generic way from the
  // `rest` object which is ... `item` data without `Material` property.
  const compoundKey = Object.keys(rest).join('');

  // access or create the `Material` specific group.
  const group = index[materialValue] ??= { Material: materialValue };
  // access or create the `CodeQuantity` specific list.
  const list = group[compoundKey] ??= [];

  // push the `rest` object into the `CodeQuantity` specific list.
  list.push(rest);
  return index;
}

const materialDataItemList = [{
  "Material": "123",
  "Code": "AAA",
  "Quantity": 1
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "456",
  "Code": "AAA",
  "Quantity": 7
}];

const totalCodeQuantityItemList = Object
  .values(
    materialDataItemList.reduce(createMaterialGroup, {})
  )
  .map(materialGroup => {
    materialGroup.CodeQuantity = Object.values(
      materialGroup.CodeQuantity.reduce(collectTotalCodeQuantity, {})
    );
    return materialGroup;
  });

console.log({ totalCodeQuantityItemList });
console.log(
  '... intermediate process result ... map of (grouped) material items ...',
  materialDataItemList.reduce(createMaterialGroup, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Edit

Moin! how can I add a new key "CodeQuantityGesamt" where the values of all "Quantity" of a "Material" is summed? example:

{
  "Material": "123",
  "CodeQuantityGesamt": 5,
  "CodeQuantity": [{
    "Code": "AAA",
    "Quantity": 1
  }, {
    "Code": "BBB",
    "Quantity": 4
  }]
}

– InFlames82

This can be achieved by changing the mapping task from ...

.map(materialGroup => {
  materialGroup.CodeQuantity = Object.values(
    materialGroup.CodeQuantity.reduce(collectTotalCodeQuantity, {})
  );
  return materialGroup;
});

... to e.g. ...

.map(materialGroup => Object.assign(
  materialGroup,
  materialGroup.CodeQuantity.reduce(collectCodeQuantityTotals, {})
));

... together with adapting the implementation and changing the name of the former collectTotalCodeQuantity reducer function which becomes collectCodeQuantityTotals.

function collectCodeQuantityTotals(quantities, item, idx, arr) {
  const { Code, Quantity } = item;
  let {
    CodeQuantityTotal = 0,
    CodeQuantity = [],
    index = {},
  } = quantities;

  // access ...
  let codeQuantityItem = index[Code];
  // ... or create the `Code` specific code quantity item.
  if (!codeQuantityItem) {
    codeQuantityItem = index[Code] = { Code, Quantity: 0 };

    // push a newly created item into the CodeQuantity list.
    CodeQuantity.push(codeQuantityItem);
  }
  // sum up a `Code` specific code quantity item's quantity value.
  codeQuantityItem.Quantity += Quantity;

  // sum up the total (or overall) code quantity value.
  CodeQuantityTotal += Quantity;

  // return a full collector/accumulator object as long as
  // the reduce task takes place because `index` is needed
  // as lookup, but return, as the final result, an object
  // without the `index` property.
  return (idx < arr.length - 1)
    && { CodeQuantityTotal, CodeQuantity, index }
    || { CodeQuantityTotal, CodeQuantity };
}
function createMaterialGroup(index, item) {
  const { Material: materialValue, ...rest } = item;
  // create the `CodeQuantity` key in a more generic way from the
  // `rest` object which is ... `item` data without `Material` property.
  const compoundKey = Object.keys(rest).join('');

  // access or create the `Material` specific group.
  const group = index[materialValue] ??= { Material: materialValue };
  // access or create the `CodeQuantity` specific list.
  const list = group[compoundKey] ??= [];

  // push the `rest` object into the `CodeQuantity` specific list.
  list.push(rest);
  return index;
}

const materialDataItemList = [{
  "Material": "123",
  "Code": "AAA",
  "Quantity": 1
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "456",
  "Code": "AAA",
  "Quantity": 7
}];

const totalCodeQuantityItemList = Object
  .values(
    materialDataItemList.reduce(createMaterialGroup, {})
  )
  .map(materialGroup => Object.assign(
    materialGroup,
    materialGroup.CodeQuantity.reduce(collectCodeQuantityTotals, {})
  ));

console.log({ totalCodeQuantityItemList });
console.log(
  '... intermediate process result ... map of (grouped) material items ...',
  materialDataItemList.reduce(createMaterialGroup, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • Moin! how can I add a new key "CodeQuantityGesamt" where the values of all "Quantity" of a "Material" is summed? example: { "Material": "123", "CodeQuantityGesamt": 5, "CodeQuantity": [ { "Code": "AAA", "Quantity": 1 }, { "Code": "BBB", "Quantity": 4 } ] } – InFlames82 Feb 18 '22 at 07:57
  • @InFlames82 ... Tach och! ... one has to change the 2nd task, the final mapping, according to the new requirement(s). The edit of the above answer does demonstrate how it can be achieved. And in order to comment on the downvote ... _**the easy refactoring of the first implementation also proves the reliability/robustness of the underlying approach**_. – Peter Seliger Feb 18 '22 at 12:24
-2

try this

const arr = [{
  "Material": "123",
  "Code": "AAA",
  "Quantity": 1
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "456",
  "Code": "CCC",
  "Quantity": 7
}];

let group = arr.reduce((r, a) => {
  r[a.Material] = [...r[a.Material] || [], a];
  return r;
}, {});



 var newArr = Object.keys(group).map(key => {
        var elem = group[key];
        var b = elem.map(e => {
            return {
                Code: e.Code,
                Quantity: e.Quantity

            }
        });
        var filteredArr = [];
        b.map(i => {
            var item = filteredArr.find(n => n.Code == i.Code);
            if (item) {
                item.Quantity += i.Quantity;
            } else {
                filteredArr.push(i);
            }
        })
        return {
            Material: key,
            CodeQuantity: filteredArr
        }
    });
    console.log(newArr);
   
Salim Baskoy
  • 591
  • 5
  • 11
-2

Updated the solution to match your expectation. This this. Hope it solves your use case.

const arr = [{
  "Material": "123",
  "Code": "AAA",
  "Quantity": 1
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "123",
  "Code": "BBB",
  "Quantity": 2
}, {
  "Material": "456",
  "Code": "CCC",
  "Quantity": 7
}]

var arrResultExpected = [{
  "Material": "123",
  "CodeQuantity": [{
    "Code": "AAA",
    "Quantity": 1
  }, {
    "Code": "BBB",
    "Quantity": 4
  }]
}, {
  "Material": "456",
  "CodeQuantity": [{
    "Code": "CCC",
    "Quantity": 7
  }]
}]

//console.log(arr)
//console.log("ExpecredResult:", arrResultExpected)

const materialIds = arr.map(a => a.Material);
//console.log(materialIds);
const dedupedMaterialIds = [...new Set(materialIds)]
//console.log(dedupedMaterialIds);

const needArr = dedupedMaterialIds.map(entry => {
  const matchingEntries = arr.filter(arrEntry => entry === arrEntry.Material);
  //console.log(matchingEntries);

  var summedResult = [];
  matchingEntries.reduce(function(res, value) {
    if (!res[value.Code]) {
      res[value.Code] = {
        Quantity: 0,
        Code: value.Code
      };
      summedResult.push(res[value.Code])
    }
    res[value.Code].Quantity += value.Quantity
    return res;
  }, {});
  //console.log(summedResult);
  return {
    Material: entry,
    CodeQuantity: [...summedResult]
  };
});


console.log(needArr);
Mahesh Bongani
  • 680
  • 7
  • 20