These are chat archives for ramda/ramda

6th
Jul 2017
Philipp Wille
@Yord
Jul 06 2017 05:48
Thanks for the answers, guys!
Francisco
@franciscotln
Jul 06 2017 07:23
@luishendrix92 what's your data structure ? :- )
something like this?
const collection = [
  { Director: 'Some Guy', imdbRating: '0.5' },
  { Director: 'Christopher Nolan', imdbRating: '9.8' }
];
Francisco
@franciscotln
Jul 06 2017 07:29
if so, I don't see the point in having "mean" (average)
Kurt Milam
@kurtmilam
Jul 06 2017 07:31
@franciscotln I guess it's a collection of films with a director and imdbRating property, and he's trying to get the average rating of all films by a specific director.
Francisco
@franciscotln
Jul 06 2017 07:32
I c
Kurt Milam
@kurtmilam
Jul 06 2017 07:35
So:
const collection = [
  { filmName: 'Some Film', Director: 'Some Guy', imdbRating: '0.5' },
  { filmName: 'The Dark Knight', Director: 'Christopher Nolan', imdbRating: '9.0' },
  { filmName: 'Inception', Director: 'Christopher Nolan', imdbRating: '8.8' }
]
averageNolanRating( collection ) //-> 8.9
Francisco
@franciscotln
Jul 06 2017 07:40
then I think this should be fine : - )
const averageNolanRating = compose(
  mean, // mean works with an array of strings ['1', '2', '3']
  map(propOr(0, 'imdbRating')), // avoids NaN if the prop is not present
  filter(propEq('Director', 'Christopher Nolan'))
);

const collection = [
  { id: 1, Director: 'Some Guy', imdbRating: '2' },
  { id: 2, Director: 'Christopher Nolan', imdbRating: '4' },
  { id: 3, Director: 'Christopher Nolan', imdbRating: '6' },
  { id: 4, Director: 'Some Other Guy', imdbRating: '10' },
];

averageNolanRating(collection);
// => 5 as number
@luishendrix92 :arrow_heading_up:
Kurt Milam
@kurtmilam
Jul 06 2017 08:04
I'd prefer a pointful solution to this problem that offers more composability and remains readable. I don't think it's a good idea to sacrifice composability or readability on the altar of being point free.
const collection = [
  { filmName: 'Some Film', Director: 'Some Guy', imdbRating: '0.5' },
  { filmName: 'The Dark Knight', Director: 'Christopher Nolan', imdbRating: '9.0' },
  { filmName: 'Inception', Director: 'Christopher Nolan', imdbRating: '8.8' }
]

const averageRatingByProp = 
  whichProp => match =>
    compose(
      mean,
      map( propOr( 0, 'imdbRating' ) ),
      filter( propEq( whichProp, match ) )
    )

 averageRatingByProp( 'Director' )
                    ( 'Christopher Nolan' )
                    ( collection )
// -> 8.9
Ermes Enea Colella
@eecolella
Jul 06 2017 08:42
@kurtmilam thanks for the tip about partial.lenses. It looks pretty cool!
Kurt Milam
@kurtmilam
Jul 06 2017 08:44
@eecolella You're welcome! I really enjoy working with that library. Don't forget to drop by the calmm-js gitter if you have any questions!
Kurt Milam
@kurtmilam
Jul 06 2017 08:53
:point_up: July 6, 2017 10:04 AM >> More examples using a more pointful style, to show how it allows you to increase reusability and flexibility through further abstraction while maintaining readability.
Julio Borja Barra
@juboba
Jul 06 2017 11:07
is there something like a containsBy?
I want to know if an array contains an object with some property
findIndex(propEq(...)) is kinda ugly...
alpox
@alpox
Jul 06 2017 11:30
@juboba maybe any instead of findIndex?
Julio Borja Barra
@juboba
Jul 06 2017 11:50
right, thanks @alpox
Ricardo Ambrogi
@KadoBOT
Jul 06 2017 13:32
how do I merge the payload with the state using ramda?
(state, payload) => ({ ...state, characters: payload, isLoading: false })
It's quite easy with mergeDeepLeft when the function does not have a second arg. Now I'm kinda lost.
alpox
@alpox
Jul 06 2017 13:49
@KadoBOT Just a shot in the dark, but what about something like:
(state, action) =>
    evolve({
        isLoading: F,
        characters: mergeDeepLeft(action.payload)
    }, state)
Ricardo Ambrogi
@KadoBOT
Jul 06 2017 13:54
That should work, but I was trying to solve it without opening a function
Like this:
  // characters.request]: state => ({...state, isLoading: true}),
  [characters.request]: mergeDeepLeft({ isLoading: T }),
Francisco
@franciscotln
Jul 06 2017 14:24
@kurtmilam with this solution you lose the curry capabilities because now you have to call it with (prop)(valueToMatch), you can't call it with 2 arguments if you want to: (prop, valueToMatch), so it's better in this case curry((whichProp, valueToMatch) => { ... })
BTW in this case the point free solution is still readable, it's just like reading a text in English: take the mean of an array of imdbRatings for all objects which have the property 'Director' equal to 'Christopher Nolan' : -) That's just my opinion.
Michael Rosata
@mrosata
Jul 06 2017 14:33
@alpox evolve won't work if the state doesn't already have characters prop. @KadoBOT why not const (state, payload) => mergeDeepRight(state, { characters: payload, isLoading: false })
alpox
@alpox
Jul 06 2017 14:38
@mrosata Right, i keep forgetting about that :-/ i was already once thinking about creating a version of it which doesn't ignore it, just for the use in reducers
Michael Rosata
@mrosata
Jul 06 2017 14:42
@alpox @KadoBOT maybe this, (it takes prop payload where my first function just took the entire action, and it's point free)
useWith(mergeDeepRight, [
  assoc('isLoading', false),
  applySpec({ characters: prop('payload') })
])
I think in reducers sometimes it's easier not to do them point free. Too much of a headache when you come back a few weeks later trying to figure them all out. That's just me though maybe
alpox
@alpox
Jul 06 2017 14:44
Yes i prefer it pointfull there
Michael Rosata
@mrosata
Jul 06 2017 14:44
:smile:
alpox
@alpox
Jul 06 2017 14:44
It gets harder to read when trying it pointfree
Michael Rosata
@mrosata
Jul 06 2017 14:46
agreed
Ricardo Ambrogi
@KadoBOT
Jul 06 2017 14:53
yeah I agree that is harder to read, just exercising. And @mrosata this wont work. 'payload' is not a prop, just the name of the second argument, which is an array of objects. Also, state (the 1st arg) might contain other values. So what I need is: create a new object, and merge it with the values of the 1st arg. Then, create a second object with a characters key. Set the characters to the value of the 2nd arg. Then merge the first object, with the second. Which is this (without ramda):
[characters.success]: (state, payload) => ({ ...state, characters: payload, isLoading: false }),
Michael Rosata
@mrosata
Jul 06 2017 14:55
@KadoBOT you could change prop to identity
useWith(mergeDeepRight, [
  assoc('isLoading', false),
  applySpec({ characters: identity })
])
that will set isLoading to false on state, otherwise state remains the same ie: {...state, isLoading: false}. Then applySpec sets characters to payload, so all in all it acts like
(state, payload) => mergeDeepRight({...state, isLoading: false }, { characters: payload })
Luis Felipe López G.
@luishendrix92
Jul 06 2017 15:10
@franciscotln Thank you, that's exactly what I was looking for.
Ricardo Ambrogi
@KadoBOT
Jul 06 2017 15:26
@mrosata awesome :D
Kurt Milam
@kurtmilam
Jul 06 2017 17:20
@franciscotln Regarding the curry capabilities, as you showed, it's easy to get them back by wrapping the function in curry() if you want them. I think it's mostly a question of style, and I prefer to manually curry my functions such that exactly one argument is required for each call (like Haskell, I believe). What's the material difference, after all, between calling fn( x, y ) and fn( x )( y )? If there is one, I'm not aware of it. Furthermore, I suspect but can't prove that my way would perform better, since there's no need to check the number of arguments sent into the function every time it's called.
Regarding readability, I may not have communicated clearly. Your solution is certainly readable. My point was that your solution isn't flexible and reusable enough for me. What if our objects contain ratings from several different sources, and not just from imdb? If they do, using your solution, we'd have to copy and paste the whole thing, only changing the name of the imdbRating property in each copy and pasted version. That's not DRY in my opinion. If we want a more flexible and reusable solution, and we insist on making it point-free, it is likely to quickly become more unreadable than a pointful one - see the more abstracted code sample I shared earlier for a better idea of what I mean here.
Robert Mennell
@skatcat31
Jul 06 2017 17:29

@kurtmilam manual currying does outperform Ramda's auto currying, however it will ALWAYS be curried in such a pattern. Ramdas is slower, but includes multiple pattern use. When not using Ramda's auto currying methods, you should actually design your manual currying for each individual case. An example:

//ignore ramda has this, it is an example of showing manual currying for higher performance. Where this would be needed is a matter of "if Ramda is barely to slow", which would probably be never?
function checkField( extract, requisite, badReturn ){
  return function( x ){
    if(requisite(extract(x)) return false;
    return badReturn ? badReturn : true;
  }
}

this function curries is such a way that you have your known(extractor, requisite test function, and a value for badreturn if provided) and then function it returns accepts your unknown(the object to check against). This is more useful in that you have an api of (known) -> (unknown) -> result which is what currying is supposed ot be used for.

of course if your use case is known -> unkown -> unkown -> ... -> result then currying that would be a pain...
Kurt Milam
@kurtmilam
Jul 06 2017 18:00
For known -> uknown -> unknown -> ... -> result, I'd generally use known -> [ unknowns ] -> result. I use my own pipe function, for instance: [ fns ] -> data.
Which, if I recall correctly, is how Haskell does pipe.