Using Ramda in point-free way to transform data into new format Recently I asked on SO about using point free methods to rearrange some data

The idea was to turn data in this format:

const data = [
  {
    timeline_map: {
      "2017-05-06": 770,
      "2017-05-07": 760,
      "2017-05-08": 1250,
    }
  }, {
    timeline_map: {
      "2017-05-06": 590,
      "2017-05-07": 210,
      "2017-05-08": 300,
    }
  }, {
    timeline_map: {
      "2017-05-06": 890,
      "2017-05-07": 2200,
      "2017-05-08": 1032,
    }
  }
]


Into this:

const hope = [
  ["2017-05-06", 770, 590, 890],
  ["2017-05-07", 760, 210, 2200],
  ["2017-05-08", 1250, 300, 1032],
]


The answers I got seemed kind of verbose in my opinion however (though I've no idea if they are better or worse than mine I haven't checked performance yet) so I spent more time studying Ramda and have come up with a solution of my own I like a little better. However I've only been doing this a little over a week so I'm sure it can be improved.

My code: 

const datesValuesReducer = (accum, curr) =&gt; {
  if (accum.hasOwnProperty(curr[0])) {
    accum[curr[0]] = accum[curr[0]].concat(curr[1])
  } else {
    accum[curr[0]] = [curr[0], curr[1]]
  }
  return accum
}

const res = R.pipe(
  R.pluck('timeline_map'),
  R.map(R.toPairs),
  R.flatten,
  R.splitEvery(2),
  R.reduce(datesValuesReducer, {}),
  R.values
)

console.log(res(data))


Three points of possible concern:

1) Omitting R.flatten and R.splitEvery(2) get pretty close to what Is being output directly from R.map(R.toPairs) so maybe there is a way to deal with that data more directly and those methods can be omitted.

2) datesValuesReducer is pretty complex, maybe it could be simplified. I'm not sure if having the accum and curr means this solution isn't entirely "point free". Thoughts?

3) Also, I use pipe in favor over compose; I just find it reads more naturally. Maybe someone has some opinions about that.

JSBIN
I like your pipeline approach in general!


  1) Omitting R.flatten and R.splitEvery(2) get pretty close to what Is being output directly from R.map(R.toPairs) so maybe there is a way to deal with that data more directly and those methods can be omitted.


Yup, you want something like R.chain here.

R.chain(R.toPairs)


is equivalent here to

R.pipe(
  R.map(R.toPairs),
  R.flatten,
  R.splitEvery(2)
)


effectively performing a non-recursive flatten on the output of the chained function. Clojure calls this mapcat.




  2) datesValuesReducer is pretty complex, maybe it could be simplified. I'm not sure if having the accum and curr means this solution isn't entirely "point free". Thoughts?


Yeah, named arguments are essentially the "points" you're trying to avoid in the pointfree style.

I see two directions here. One is to shorten the function but add more points via destructuring:

R.reduce((acc, [k, v]) =&gt; R.assoc(k, (acc[k] || [k]).concat(v), acc),
         {})


The other is to fall further into the Ramda world and do something like this:

const f = R.pipe(
  R.pluck('timeline_map'),
  R.chain(R.toPairs),
  R.reduceBy(R.flip(R.useWith(R.append, [R.nth(1)])), [], R.head),
  R.toPairs,
  R.map(R.unnest)
)


(The last two functions could probably be turned into a reduce as well if you're determined.)

This is fully pointfree, but seems kinda deliberately obtuse, even if the reduceBy reducer were to be pulled out into a named function. If you want an opinion I'd say stick the entire thing in a function that takes in the base keyword (eg. 'timeline_map'), and then use whatever you find most readable inside that function, without striving for total pointfree purity.

If you want more opinions, arguably there's a sweet spot of, say, 60-70% pointfree where you gain the most from the style. Beyond that, things can become increasingly contorted, and you end up with, well, R.reduceBy(...R.useWith...