1

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.

Dacomis
  • 358
  • 5
  • 13
  • Does this answer your question? [Reduce Array of Array of Objects Using Ramda](https://stackoverflow.com/questions/65602396/reduce-array-of-array-of-objects-using-ramda) – jsN00b Mar 30 '22 at 10:37
  • It was a good starting point but I don't know how to add 2 properties in that combine under ```map(reduce(combine, {})) ``` – Dacomis Mar 30 '22 at 10:43

4 Answers4

2

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}
A1exandr Belan
  • 4,442
  • 3
  • 26
  • 48
2

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)
  }))
)
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
1

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>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

This solution (using JS and not rambda) attempts to use BigInt and provides the value into a prop named valueB.

const groupAndSum = arr => (
  Object.values(
    arr.flat().reduce(
      (acc, {index, value, participants}) => ({
        ...acc,
        [index]: {
          index,
          valueB: BigInt((acc[index]?.valueB ?? 0)) + BigInt(value), // valueB as big-int
          value: (acc[index]?.value || 0) + +value, // value as a "Number"
          participants: (acc[index]?.participants ?? 0) + +participants
        }
      }),
      {}
    )
  ).map( // to display big-int in the console within stack-snippets
    ({valueB, ...rest}) => ({valueB: valueB.toString(), ...rest})
  )
);

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(groupAndSum(data));

Explanation

  • Object.values() is used to extract the values of the resulting-object from below operations
  • First, the input array is flattened to remove the nesting
  • Next, .reduce is used to iterate through the array and generate a result-object
  • acc is the accumulator/aggregator object
  • Each index is either added into acc as a new key with the value being the actual object
  • If the index was already present in acc, then the value and participants props are summed-up.
  • valueB is a new prop to store the BigInt format of value

An additional .map() is used to make valueB displayable within the console.log on stack-snippets. This may not be required in the original code - so, please ignore.

jsN00b
  • 3,584
  • 2
  • 8
  • 21