1

I have an array of array of objects. I want to reduce that to an array of object and adding one more property to each object. The sample input is:

const data = [
    [
        {name:"a", val:5},
        {name:"b", val:10},
        {name:"c", val:20},
        {name:"d", val:50},
        {name:"e", val:100}
    ],
    [
        {name:"a", val:0},
        {name:"b", val:20},
        {name:"c", val:30},
        {name:"d", val:40},
        {name:"e", val:10}
    ],
    [
        {name:"a", val:60},
        {name:"b", val:50},
        {name:"c", val:40},
        {name:"d", val:70},
        {name:"e", val:30}
    ]
];

And the Output should be:

[{name: 'a', val: 65, rank: 'si'},
 {name: 'b', val: 80, rank: 'dp'},
 {name: 'c', val: 90, rank: 'en'}
 {name: 'd', val: 160, rank: 'fr'}]

Rank is static text means for a, it will always be "si" How can I achieve this using ramda?

newbie
  • 530
  • 1
  • 9
  • 36
  • 1
    Are we to guess as to how the `rank` is generated? Grouping and summing is covered by a few different questions: [using ramda group by property and sum results on specified property](https://stackoverflow.com/q/45739479/215552), [Grouping and summing in Ramda.js](https://stackoverflow.com/q/27214451/215552), [How to group by a key and sum other keys with Ramda?](https://stackoverflow.com/q/60423794/215552) – Heretic Monkey Jan 06 '21 at 20:01
  • that is static text. – newbie Jan 06 '21 at 20:02
  • I am assuming the a, b, c, d in the resulting array are the sum of all as, bs, cs, ds in the original array but since your `b` total is incorrect and should be 80 instead of 90 if you follows that pattern, I have to ask are those actually sums or is there some other algorithm? – codemonkey Jan 06 '21 at 20:05
  • @codemonkey yes, I have corrected that :) That is actually sum – newbie Jan 06 '21 at 20:06

1 Answers1

3

You can convert flatten all sub arrays to a single array, group by the name, and then map the groups, and reduce each group to a single object using R.mergeWithKey to add the val property. Convert back to an array using R.values, and map to add the static ranks property by name.

Note that you must create a Map or a dictionary object to take the rank by name from.

const { mergeWithKey, pipe, flatten, groupBy, prop, map, reduce, values } = R

const ranks = new Map([['a', 'si'], ['b', 'dp'], ['c', 'en'], ['d', 'fr']])

// merge deep and combine val property values
const combine = mergeWithKey((k, l, r) => k == 'val' ? l + r : r)

const mergeData = pipe(
  flatten, // flatten to a single array
  groupBy(prop('name')), // group by the name
  map(reduce(combine, {})), // combine each group to a single object
  values, // convert back to array
  map(o => ({ ...o, rank: ranks.get(o.name) })), // add the static rank property
)

const data = [[{"name":"a","val":5},{"name":"b","val":10},{"name":"c","val":20},{"name":"d","val":50},{"name":"e","val":100}],[{"name":"a","val":0},{"name":"b","val":20},{"name":"c","val":30},{"name":"d","val":40},{"name":"e","val":10}],[{"name":"a","val":60},{"name":"b","val":50},{"name":"c","val":40},{"name":"d","val":70},{"name":"e","val":30}]]

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
  • Just one thing, in your solution, you are also returning `name: "e"` object as well. That is not required – newbie Jan 06 '21 at 20:39
  • 1
    You can always add a filter after flattening to remove unwanted objects. – Ori Drori Jan 06 '21 at 20:52
  • One thing more, you have used `map` whereas we can use `object` there. Is there a specific reason you used `map`? – newbie Jan 06 '21 at 20:58
  • 1
    Maps are more idiomatic, and the key can be any type of value, but in this case is mostly a personal preference. – Ori Drori Jan 06 '21 at 21:11