0

I need to group items from an array by batchNumber and sum their values.

Currently I am using ramda, but I am able to group but not to transform the result.

Could you please provide me an example using ramda.js?

Live Example:

const input = [
    {
        batchNumber: 'a',
        scrapAmount: 5
    },
    {
        batchNumber: 'a',
        scrapAmount: 10
    },
    {
        batchNumber: 'b',
        scrapAmount: 1
    },
    {
        batchNumber: 'b',
        scrapAmount: 2
    },
    {
        scrapAmount: 7
    },
    {
        scrapAmount: 3
    }
]

const byBatchNumber = R.groupBy((batch) => batch.batchNumber);


console.log(byBatchNumber(input))

/* result wanted

const output = [
    {
        batchNumber: 'a',
        scrapAmount: 15
    },
    {
        batchNumber: 'b',
        scrapAmount: 3
    },
    {
        batchNumber: undefined,
        scrapAmount: 10
    },
]

*/
Radex
  • 7,815
  • 23
  • 54
  • 86
  • 1
    Use this method: `var result = []; input.reduce(function (res, value) { if (!res[value.batchNumber]) { res[value.batchNumber] = { scrapAmount: 0, batchNumber: value.batchNumber }; result.push(res[value.batchNumber]) } res[value.batchNumber].scrapAmount += value.scrapAmount return res; }, {});` – Muhammet Can TONBUL Feb 01 '18 at 08:15
  • 1
    ``` R.map( reduce( (acc, curr) => acc + curr.scrapAmount, 0 ) )(byBatchNumber(input)) ``` – jgr0 Feb 01 '18 at 08:32
  • 1
    @NinaScholz: This does not look like a duplicate of that one, which is asking for a more sophisticated merging of objects. This one can probably be handled with `pipe(groupBy(prop('batchNumber')), map(pluck('scrapAmount')), map(sum))`. – Scott Sauyet Feb 01 '18 at 15:36
  • @ScottSauyet, please add this as answer. – Nina Scholz Feb 01 '18 at 15:39

2 Answers2

2

You groupWith() by checking that the batchNumber is equal with eqProps(). Then map() each subarray, apply mergeWithKey() to all objects, and add() the values of the scrapAmount field:

const { compose, groupWith, eqProps, map, apply, mergeWithKey, add } = R;

const input = [{"batchNumber":"a","scrapAmount":5},{"batchNumber":"a","scrapAmount":10},{"batchNumber":"b","scrapAmount":1},{"batchNumber":"b","scrapAmount":2},{"scrapAmount":7},{"scrapAmount":3}]

const byBatchNumber = compose(
  map(apply(mergeWithKey((k, l, r) => k === 'scrapAmount' ? add(l, r) : r))),
  groupWith(eqProps('batchNumber'))
)

console.log(byBatchNumber(input))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
1

A fairly simple version would be the following:

const {pipe, groupBy, prop, map, pluck, sum} = R;

const input = [
    {batchNumber: 'a', scrapAmount: 5},
    {batchNumber: 'a', scrapAmount: 10},
    {batchNumber: 'b', scrapAmount: 1},
    {batchNumber: 'b', scrapAmount: 2},
    {scrapAmount: 7},
    {scrapAmount: 3}
]

const totalScrap = pipe(
  groupBy(prop('batchNumber')), 
  map(pluck('scrapAmount')), 
  map(sum)
)

console.log(totalScrap(input))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

Note first that this simplifies R.groupBy((batch) => batch.batchNumber) to R.groupBy(R.prop('batchNumber')); it' the same functionality, just expressed more concisely.

This is as far as I would go, because I believe this to be the most helpful output format for the work I usually do, namely something like this:

{"a": 15, "b": 3, "undefined": 10}

But rereading your required output, it might take two more steps:

const {pipe, groupBy, prop, map, pluck, sum, toPairs, zipObj} = R;

const input = [
    {batchNumber: 'a', scrapAmount: 5},
    {batchNumber: 'a', scrapAmount: 10},
    {batchNumber: 'b', scrapAmount: 1},
    {batchNumber: 'b', scrapAmount: 2},
    {scrapAmount: 7},
    {scrapAmount: 3}
]

const totalScrap = pipe(
  groupBy(prop('batchNumber')), 
  map(pluck('scrapAmount')), 
  map(sum),
  toPairs,
  map(zipObj(['batchNumber', 'scrapAmount']))
)

console.log(totalScrap(input))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

One thing this doesn't do is to generate an item with batchNumber: undefined, and instead returns one with batchNumber: "undefined" (a string.) While this could be fixed, it's an ugly step, and I don't see a real gain. And likely solutions would then fail if you had one with a value of "undefined". If this is really a show-stopper, you could obviously process these before the last step in that pipeline.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103