I have an array of objects as below.
I would like to group those with the same bank
, gender
, age
, and state
together.
I'd like to sum their depositAmount
and withdrawalAmount
together, respectively, and count each group as well. (user
is excluded in the output)
Example Array
[{
"bank": "bank1",
"user": "u1",
"depositAmount": 12,
"withdrawalAmount": 33,
"gender": "male",
"age": 38,
"state": "Arizona"
},
{
"bank": "bank1",
"user": "u2",
"depositAmount": 25,
"withdrawalAmount": 44,
"gender": "male",
"age": 38,
"state": "Arizona"
},
{
"bank": "bank2",
"user": "u3",
"depositAmount": 10,
"withdrawalAmount": 0,
"gender": "male",
"age": 38,
"state": "Florida"
}]
Expected Output
[{
"bank": "bank1"
"depositAmount": 27,
"withdrawalAmount": 77,
"gender": "male",
"age": 38,
"state": "Arizona"
"count": 2
},
{
"bank": "bank2"
"depositAmount": 10,
"withdrawalAmount": 0,
"gender": "male",
"age": 38,
"state": "Florida"
"count": 1
}]
CodePudding user response:
A generic solution to this problem was to implement a configurable reducer-function for the ... "It groups, merges and aggregates" task.
In order to cover the OP's requirements, one provides an accordingly tailored custom collector-object as the reduce
-method's initial value.
The generic reducer expects to get provided a createKey
, a createMerger
and an aggregate
function as well as a Map
based lookup
and an empty result
-array where the latter, once the reduce task has been run, will hold all the grouped/merged and aggregated objects.
As for the OP's specific requirements the provided functions are ...
OP ...
"I would like to group those with the same
bank
,gender
,age
, andstate
together."createKey: ({ bank, gender, age, state }) => [bank, gender, age, state].join('_')
OP ...
"I'd like to sum their
depositAmount
andwithdrawalAmount
together, respectively, and count each group as well. (user
is excluded in the output)."createMerger: ({ bank, gender, age, state }) => ({ bank, gender, age, state, count: 0, depositAmount: 0, withdrawalAmount: 0 })
aggregate: (merger, { depositAmount, withdrawalAmount }) => { merger.count; merger.depositAmount = depositAmount; merger.withdrawalAmount = withdrawalAmount; }
function groupMergeAndAggregateGenerically(collector, item) {
const {
createKey, createMerger, aggregate,
lookup, result = [],
} = collector;
const key = createKey(item);
let merger = lookup.get(key) ?? null;
if (merger === null) {
merger = createMerger(item);
lookup.set(key, merger);
result.push(merger);
}
aggregate(merger, item);
return collector;
}
const sampleData = [{
"bank": "bank1",
"user": "u1",
"depositAmount": 12,
"withdrawalAmount": 33,
"gender": "male",
"age": 38,
"state": "Arizona"
}, {
"bank": "bank1",
"user": "u2",
"depositAmount": 25,
"withdrawalAmount": 44,
"gender": "male",
"age": 38,
"state": "Arizona"
}, {
"bank": "bank2",
"user": "u3",
"depositAmount": 10,
"withdrawalAmount": 0,
"gender": "male",
"age": 38,
"state": "Florida"
}];
// OP: I would like to group those with the same
// `bank`, `gender`, `age`, and `state` together.
const createKey = ({ bank, gender, age, state }) =>
[bank, gender, age, state].join('_');
// OP: I'd like to sum their `depositAmount` and
// `withdrawalAmount` together, respectively, and
// `count` each group as well. (`user` is excluded in the output).
const createMerger = ({ bank, gender, age, state }) => ({
bank, gender, age, state, count: 0, depositAmount: 0, withdrawalAmount: 0,
});
const aggregate = (merger, { depositAmount, withdrawalAmount }) => {
merger.count;
merger.depositAmount = depositAmount;
merger.withdrawalAmount = withdrawalAmount;
};
const { result: mergedData } = sampleData
.reduce(groupMergeAndAggregateGenerically, {
createKey,
createMerger,
aggregate,
lookup: new Map,
result: [],
});
console.log({ mergedData });
.as-console-wrapper { min-height: 100%!important; top: 0; }