I have this array of arrays with objects:
const data = [
[
{
index: 320,
blocks: 2,
value: '31011784785',
participants: 1222,
cost: '1286828506'
},
{
index: 319,
blocks: 0,
value: '111306385',
participants: 18,
cost: '0'
},
{
index: 318,
blocks: 0,
value: '14550473',
participants: 10,
cost: '0'
}
],
[
{
index: 320,
blocks: 1,
value: '7089001673',
participants: 492,
cost: '648196615'
},
{
index: 319,
blocks: 0,
value: '13551137',
participants: 8,
cost: '0'
},
{
index: 318,
blocks: 0,
value: '11499815',
participants: 5,
cost: '0'
}
],
[
{
index: 320,
blocks: 1,
value: '408900161',
participants: 200,
cost: '648196615'
},
{
index: 319,
blocks: 0,
value: '23551231',
participants: 10,
cost: '0'
},
{
index: 318,
blocks: 0,
value: '104324219',
participants: 5,
cost: '0'
}
]
]
I would like to make a single array with objects that will have just the properties index, value, and participants where value and participants will be the sum of the 3 arrays. Eg:
[
{
index: 320,
value: 38509686619,
participants: 1914,
},
{
index: 319,
value: 148408753,
participants: 36,
},
{
...
}
]
I would also like the value field to be a BigInt.
Inspired by this answer I made something that works but it's way too long and cumbersome.
This question is different from Reduce Array of Array of Objects Using Ramda because I need two object property values and I don't know how to do this.
Vanila JS solution:
const data = [[{"index":320,"blocks":2,"value":"31011784785","participants":1222,"cost":"1286828506"},{"index":319,"blocks":0,"value":"111306385","participants":18,"cost":"0"},{"index":318,"blocks":0,"value":"14550473","participants":10,"cost":"0"}],[{"index":320,"blocks":1,"value":"7089001673","participants":492,"cost":"648196615"},{"index":319,"blocks":0,"value":"13551137","participants":8,"cost":"0"},{"index":318,"blocks":0,"value":"11499815","participants":5,"cost":"0"}],[{"index":320,"blocks":1,"value":"408900161","participants":200,"cost":"648196615"},{"index":319,"blocks":0,"value":"23551231","participants":10,"cost":"0"},{"index":318,"blocks":0,"value":"104324219","participants":5,"cost":"0"}]];
const result = Object.values(data.flat()
.reduce((acc, { index, value, participants }) => {
acc[index] ??= { index, value: 0, participants: 0 };
acc[index].value += Number(value);
acc[index].participants += Number(participants);
return acc;
}, {}));
console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}
One Ramda approach:
const convert = pipe (
transpose,
map (xs => ({
index: xs [0] .index,
value: xs .reduce ((a, {value}) => a + Number (value), 0),
participants: sum (pluck ('participants') (xs))
}))
)
const data = [[{index: 320, blocks: 2, value: "31011784785", participants: 1222, cost: "1286828506"}, {index: 319, blocks: 0, value: "111306385", participants: 18, cost: "0"}, {index: 318, blocks: 0, value: "14550473", participants: 10, cost: "0"}], [{index: 320, blocks: 1, value: "7089001673", participants: 492, cost: "648196615"}, {index: 319, blocks: 0, value: "13551137", participants: 8, cost: "0"}, {index: 318, blocks: 0, value: "11499815", participants: 5, cost: "0"}], [{index: 320, blocks: 1, value: "408900161", participants: 200, cost: "648196615"}, {index: 319, blocks: 0, value: "23551231", participants: 10, cost: "0"}, {index: 318, blocks: 0, value: "104324219", participants: 5, cost: "0"}]]
console .log (convert (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
<script> const {pipe, transpose, map, sum, pluck, reduce, applySpec} = R </script>
We start with transpose, which turns this into a much easier to work with format. Then for each new row, we grab the index from the first record, and sum up both the participants and the values.
Because BigInt's don't show well in the SO console, I skipped them, but the change is trivial:
- value: xs .reduce ((a, {value}) => a + Number (value), 0),
+ value: xs .reduce ((a, {value}) => a + BigInt(value), BigInt(0)),
A point-free version, if you have that fetish, is not much harder:
const convert = pipe (
transpose,
map (applySpec ({
index: pipe (head, prop ('index')),
value: reduce ((a, {value}) => a + Number (value), 0),
// value: reduce ((a, {value}) => a + BigInt (value), BigInt (0)),
participants: pipe (pluck ('participants'), sum)
}))
)
After flattening, the array of arrays, you should map and pick the wanted properties and convert values to a number, and then group by, and combine each group to a single object.
const { mergeWithKey, pipe, flatten, map, pick, evolve, groupBy, prop, reduce, values } = R
// merge deep and combine properties value
const combine = mergeWithKey((k, l, r) => k === 'value' || k === 'participants' ? l + r : r)
const mergeData = pipe(
flatten, // flatten to a single array
map(pipe(
pick(['index', 'value', 'participants']), // pick wanted properties
evolve({ value: Number }) // convert values to a number
)),
groupBy(prop('index')), // group by the name
map(reduce(combine, {})), // combine each group to a single object
values, // convert back to array
)
const data = [[{"index":320,"blocks":2,"value":"31011784785","participants":1222,"cost":"1286828506"},{"index":319,"blocks":0,"value":"111306385","participants":18,"cost":"0"},{"index":318,"blocks":0,"value":"14550473","participants":10,"cost":"0"}],[{"index":320,"blocks":1,"value":"7089001673","participants":492,"cost":"648196615"},{"index":319,"blocks":0,"value":"13551137","participants":8,"cost":"0"},{"index":318,"blocks":0,"value":"11499815","participants":5,"cost":"0"}],[{"index":320,"blocks":1,"value":"408900161","participants":200,"cost":"648196615"},{"index":319,"blocks":0,"value":"23551231","participants":10,"cost":"0"},{"index":318,"blocks":0,"value":"104324219","participants":5,"cost":"0"}]]
const results = mergeData(data)
console.log(results)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>