Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using ramda group by property and sum results on specified property

I need help transforming an array of objects using ramda; I'd like to

  1. group by a specified property
  2. sum another property on the resulting set

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);
like image 886
Jonathan Portorreal Avatar asked Aug 17 '17 15:08

Jonathan Portorreal


2 Answers

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.

like image 137
Scott Sauyet Avatar answered Nov 15 '22 10:11

Scott Sauyet


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    
}                  
like image 36
Shaun Luttin Avatar answered Nov 15 '22 10:11

Shaun Luttin