Reputation: 2471
I have multiple arrays of objects with a string property "CountType"
and a number property "ItemCount"
.
The objects must be added up to calculate the total of all items by "CountType"
property.
For example, I need to know the total number of Volumes, Sheets, Photos, etc.
Attempt #1: This code I attempted returns 0. Possibly that is because I need to loop through the outer array to do the comparison across the nested arrays.
function sumByProperty(items, prop) {
if (items == null) {
return 0;
}
return items.reduce(function (a, b) {
return b[prop] == null ? a : a + b[prop];
}, 0);
}
INPUT
Item count
[
[
{"CountType":"Volumes","ItemCount":3},
{"CountType":"Sheets","ItemCount":6},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":1},
{"CountType":"Other","ItemCount":2}
],
[
{"CountType":"Volumes","ItemCount":1},
{"CountType":"Sheets","ItemCount":1},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":0},
{"CountType":"Other","ItemCount":1}
],
[
{"CountType":"Volumes","ItemCount":1},
{"CountType":"Sheets","ItemCount":0},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":4},
{"CountType":"Other","ItemCount":5}
]
]
DEISRED OUTPUT
Total count
[
{"CountType":"Volumes","ItemCount":5},
{"CountType":"Sheets","ItemCount":7},
{"CountType":"Photos","ItemCount":9},
{"CountType":"Boxes","ItemCount":5},
{"CountType":"Other","ItemCount":8}
]
UPDATE: Here is how I am trying to run the function:
https://jsfiddle.net/yd051o76/2/
Upvotes: 3
Views: 1085
Reputation: 12980
Here's an example using 2 reduce functions with a for ... in ...
to merge the results back into the parent reducer.
The trick is to make sure you initialize your accumulator as null so you can define your own. Then, it's just a matter of reducing the array of arrays and having another reducer to run the array of objects. Once done, you just need to merge the result of the inner array back into the parent. (Might be a better way to do this part)
Fiddle: https://jsfiddle.net/mswilson4040/jcp9x6ba/26/
const json = [
[
{"CountType":"Volumes","ItemCount":3},
{"CountType":"Sheets","ItemCount":6},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":1},
{"CountType":"Other","ItemCount":2}
],
[
{"CountType":"Volumes","ItemCount":1},
{"CountType":"Sheets","ItemCount":1},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":0},
{"CountType":"Other","ItemCount":1}
],
[
{"CountType":"Volumes","ItemCount":1},
{"CountType":"Sheets","ItemCount":0},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":4},
{"CountType":"Other","ItemCount":5}
]
];
const counts = json.reduce( (acc, currentArray) => {
acc = acc ? acc : {};
const arrCounts = currentArray.reduce( (_acc, _item) => {
_acc = _acc ? _acc : {};
_acc[_item.CountType] = _acc[_item.CountType] ? _acc[_item.CountType] + _item.ItemCount : _item.ItemCount;
return _acc;
}, null);
for (const item in arrCounts) {
acc[item] = acc[item] ? acc[item] + arrCounts[item] : arrCounts[item];
}
return acc;
}, null);
console.log(counts);
Upvotes: 1
Reputation: 92735
Try (data is input, h = {}, the r is result);
data.map(a=> a.map(x=> h[x.CountType]=x.ItemCount+(h[x.CountType]||0) ))
r= Object.keys(h).map(k=> ({CountType:k, ItemCount:h[k] }) );
let data = [[
{"CountType":"Volumes","ItemCount":3},
{"CountType":"Sheets","ItemCount":6},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":1},
{"CountType":"Other","ItemCount":2}
],
[
{"CountType":"Volumes","ItemCount":1},
{"CountType":"Sheets","ItemCount":1},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":0},
{"CountType":"Other","ItemCount":1}
],
[
{"CountType":"Volumes","ItemCount":1},
{"CountType":"Sheets","ItemCount":0},
{"CountType":"Photos","ItemCount":3},
{"CountType":"Boxes","ItemCount":4},
{"CountType":"Other","ItemCount":5}
]];
let r,h = {};
data.map(a=> a.map(x=> h[x.CountType]=x.ItemCount+(h[x.CountType]||0) ))
r= Object.keys(h).map(k=> ({CountType:k, ItemCount:h[k] }) );
console.log(r);
Upvotes: 0
Reputation: 138557
Possibly that is because I need to loop through the outer array to do the comparison across the nested arrays.
Indeed, you could flatten the array:
function sumByProperty(items, prop) {
return items.flat().reduce(function (a, b) {
return b[prop] == null ? a : a + b[prop];
}, 0);
}
const result = [
{ CountType: "Volumes", ItemCount: sumByProperty(input, "Volumes"), },
//...
];
But I'd rather group dynamically using a hashtable, that way you only have to iterate once and you don't need to name all properties:
const hash = new Map();
for(const { CountType, ItemCount } of input.flat())
hash.set(CountType, (hash.get(CountType) || 0) + ItemCount);
const result = [...hash.entries()].map(([CountType, ItemCount]) => ({ CountType, ItemCount }));
Upvotes: 4