I need help transforming an array of objects using ramda; I'd like to
given an array like this:
var arr = [
{
title: "scotty",
age: 22,
score: 54,
hobby: "debugging"
}, {
title: "scotty",
age: 22,
score: 19,
hobby: "debugging"
}
, {
title: "gabriel",
age: 40,
score: 1000
}
];
if I want to group by title
and sum on age
it should return the following summary of values
var arr = [
{
title: "scotty",
age: 44,
hobby: "debugging",
}
, {
title: "gabriel",
age: 40,
score: 1000
}
];
when the unspecified property's differ in value they should be omitted, but if unspecified property's are the same in value it should remain in the final result.
** My Solution **
/*
* [Student]
*/
var arr = [
{
title: "scotty",
age: 22,
score: 54,
hobby: "debugging"
}, {
title: "scotty",
age: 22,
score: 19,
hobby: "debugging"
}
, {
title: "gabriel",
age: 40,
score: 1000
}
];
/*
* String -> [[Student]] -> [Student]
*/
var sumOnProperty = function(property, v){
var sum = (x,y) => x[property] + y[property];
var new_array = [];
v.forEach(arr => {
if(arr.length > 1){
arr[0]["age"] = arr.reduce(sum)
new_array.push(arr[0]);
} else {
if(arr.length != 0){
new_array.push(arr[0]);
}
}
})
return new_array;
}
/*
* String -> String -> [Student] -> [Student]
*/
var groupsumBy = function(groupproperty, sumproperty, arr){
// create grouping
var grouping = R.groupBy(R.prop(groupproperty), arr)
// convert grouping object to array
var result1 = R.valuesIn(grouping);
// sum each grouping and flatten 2d array
var result2 = sumOnProperty(sumproperty, result1);
return result2;
}
groupsumBy("title","age",arr);
To fix your groupBy
problem you need to see that groupBy
takes a key-generation function function and not a binary predicate.
So, for instance,
const byTitle = R.groupBy(R.prop('title'));
This should get you through your current block. If you need help with the summation, let me know.
Update:
You asked for my approach. It does differ from yours a fair bit. I would probably do something like this:
const sumBy = prop => vals => reduce(
(current, val) => evolve({[prop]: add(val[prop])}, current),
head(vals),
tail(vals)
)
const groupSumBy = curry((groupOn, sumOn, vals) =>
values(map(sumBy(sumOn))(groupBy(prop(groupOn), vals)))
)
groupSumBy('title', 'age', people)
Or if I wanted it a little more concise, I might switch to:
const sumBy = prop => lift(
reduce((current, val) => evolve({[prop]: add(val[prop])}, current)
))(head, tail)
Note that sumBy
is relatively reusable. It's not perfect, because it would fail on an empty list. But in our case, we know that the output of groupBy will never create such an empty list for a key. And any version which didn't fail on an empty list would need a way to supply the default case. It simply gets ugly.
You can see this in action on the Ramda REPL.
You could probably make a more easily readable version of groupSumBy
using pipe
or compose
if you were willing to call first with the groupOn
and sumOn
values, then call the resulting function with the values, that is, if the invocation looked like this:
groupSumBy('title', 'age')(people)
// or more likely:
const foo = groupSumBy('title', age)
foo(people)
But I leave that as an exercise for the reader.
using ramda group by property and sum results on specified property
One option is to reduceBy
as follows:
import * as R from "ramda";
const arr = [
{
title: "scotty",
age: 22,
score: 54,
hobby: "debugging"
}, {
title: "scotty",
age: 22,
score: 19,
hobby: "debugging"
}
, {
title: "gabriel",
age: 40,
score: 1000
}
];
const reduction =
R.reduceBy((acc, next) => acc + next.age, 0, (x) => x.title, arr);
console.log(JSON.stringify(reduction, undefined, 2));
The output has grouped by title
and has summed the age
.
{
"scotty": 44,
"gabriel": 40
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With