0

Let's say I want to calculate the average cost:

const products = [
  {
     cost: 300
  },
  {
     cost: 700
  }
];

So first pluck the cost property, summarize them, then divide by the nr of items.

const calcualteAveragePrice = R.pipe(
      R.map(R.prop('cost') // [300, 700]
      R.sum, // 1000
      R.divide(??) // How do I divide with the number of items here??
    )

In the last step I need to divide by the number of items. Since it's point free, I cant do arr.length.

Joe
  • 4,274
  • 32
  • 95
  • 175
  • You have a few options, but being point free for the sake of being point free is costing you here. I'd convert it to a reduce and call it a day. I mean, `Array.prototype.map` passes the whole array as the third argument to the callback, and since Ramda dispatches to the map method if present on the collection it does the same, but you can't use that with piping. You could also do something kinda icky like stuff the length in a var in an outer scope and partially apply it to `R.divide`. – Jared Smith Aug 22 '19 at 22:41
  • Yeha I have working reduce function. Was curious how/if this could be done with pff. – Joe Aug 22 '19 at 22:52
  • @JaredSmith: I suppose the Ramda docs are a bit confusing on this. Ramda dispatches to that method only if it's not a type which Ramda handles itself. Here that includes Arrays, Objects, and Functions. So it won't actually dispatch. – Scott Sauyet Aug 22 '19 at 23:42
  • @ScottSauyet yes, the docs make it sound like it uses the native method when available. – Jared Smith Aug 23 '19 at 00:02

1 Answers1

4

Ramda does have a mean function (as well as a median one.) So that will make your solutions fairly simple:

const calculateAveragePrice = compose (mean, pluck( 'cost'))

const products = [{cost: 300}, {cost: 700}]

console .log (
  calculateAveragePrice(products)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {compose, mean, pluck} = R                             </script>

Or of course you could write pipe (pluck('cost'), mean) instead. I prefer compose for one-liners, and pipe for anything else, but that's just a matter of taste.

But if you didn't know that Ramda supplied this, you could write your own point-free average function with converge:

const average = converge(divide, [sum, length])

converge and its cousin useWith are designed to make it easier to write point-free versions of code. I always include a warning that point-free should rarely be a goal on its own, however. It's useful only when it serves to improve the readability of the code. Both converge and useWith tend to be questionable on that score. There is an alternative that is more standard in the FP world: the function lift:

const average = lift (divide) (sum, length)

lift transforms a function that accepts values of certain types to one that accepts containers of values of those values. And since a function that returns a value of a given type can be considered a container (squint hard if necessary), lift (f) (g, h) is something equivalent to (x) => f (g (x), h (x)). (There's more information in the SO question, Can't wrap my head around “lift” in Ramda.js.)

converge is slightly more flexible, but I always use lift if I can. It feels more standard.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • 1
    you showed me something new about `lift`ing functions :) – Mulan Aug 23 '19 at 04:57
  • 1
    And if we make `lift`'s interface variadic and name it `doA` or `doM` we have something that resembles applicative/monadic do notation pretty much. –  Aug 23 '19 at 11:01
  • @bob: I'm curious as to your comment. In once sense, I can see an obvious link, `lift (f) (g, h)` binds `f`'s two parameters to the results of applying arguments to `g` and `h`. But I don't see an obvious extension of this API. `lift` is already variadic in that `lift(f)(g, h, i, j)` ~= `(x) => f((g(x), h(x), i(x), j(x))`, but I suspect you mean something else. – Scott Sauyet Aug 23 '19 at 13:40
  • I didn't know that `lift` is already variadic, sorry. However, when I use this term I usually assume the curried form, ie. `liftAn` is equiv to `return f ap t1 ap t2 ap tn` –  Aug 23 '19 at 14:48