These are chat archives for ramda/ramda

29th
Oct 2018
Andreas Herd
@mendrik
Oct 29 2018 08:53
hi, given an array of objects, how can I replace a single item in that array where a property, ie id would match.
Riku Tiira
@rikutiira
Oct 29 2018 08:56
R.set/R.over + R.lensIndex (you can get index using R.findIndex if you don’t know it) or simply by mapping the array and only editing the item you want
Andreas Herd
@mendrik
Oct 29 2018 08:57
not something with R.update(???, newObj, myArr)
Riku Tiira
@rikutiira
Oct 29 2018 08:58
true, that too :smile:
R.update is equivalent to R.set + R.lensIndex
and R.adjust is equivalent to R.over + R.lensIndex
Andreas Herd
@mendrik
Oct 29 2018 09:00
ok
still struggling with this though
not sure how to make the connection to the 2nd update argument
Riku Tiira
@rikutiira
Oct 29 2018 09:03
you want to use findIndex and not indexBy
indexBy changes an array into an indexed object
and update takes the index as first argument, not a function
Andreas Herd
@mendrik
Oct 29 2018 09:17
right
Brad Compton (he/him)
@Bradcomp
Oct 29 2018 16:06
I tend to go for the map (when (...)) pattern for this sort of thing
Riku Tiira
@rikutiira
Oct 29 2018 16:08
I’d go for map if the index is unknown, otherwise directly updating the item so that you can avoid the unnecessary loop
of course it’d still be more performant without map since you don’t usually have to map whole array but the performance implication is at least smaller in that case
Brad Compton (he/him)
@Bradcomp
Oct 29 2018 16:12
In both cases you have a linear time algorithm. Mine is guaranteed to be single pass. Unless you're working with a very important hot path (in which case you shouldn't be using library functions at all), or lists with tens of thousands of elements (maybe get a better data structure), any performance impact is literally negligible.
I think before perf testing any performance analysis beyond big O is just guesswork
Riku Tiira
@rikutiira
Oct 29 2018 16:52
I don’t disagree with you, but even so when I have 2 equally simple ways to do a thing, I usually pick the more performant one :smile: given I have the prior knowledge which can potentially be less performant
Kurt Milam
@kurtmilam
Oct 29 2018 18:42
Another option is reduce. Iterate through the array until you find the item you're looking for and do whatever needs to be done with it, then use reduced to immediately exit the reduce in order to avoid unnecessarily looping through other items in the array.
Kurt Milam
@kurtmilam
Oct 29 2018 19:15
For instance: https://goo.gl/Ac4Y9k , or if you prefer more Ramda-like: https://goo.gl/bBsLFq
Kurt Milam
@kurtmilam
Oct 29 2018 19:26
Turned into a curried function with a name: https://goo.gl/JHzECf
Ben Briggs
@ben-eb
Oct 29 2018 19:30
@kane77 Don't know about "better" but here's two alternatives
const emptyOrEq1 = locationId => either(
  compose(isNil, always(locationId)),
  propEq('locationId', locationId)
)

const emptyOrEq2 = curryN(2, unapply(either(
  propSatisfies(isNil, 0),
  apply(propEq('locationId'))
)))
Kurt Milam
@kurtmilam
Oct 29 2018 19:59
@mendrik on the other hand, it's often nicer to work with an object indexed by the id property than an array of objects, each with an id property. This is a look at that option: https://goo.gl/FYkaVw <-- this solution is not going to be fast if you're making that round trip every time you need to update a single object in the array, but if you can switch over to using the indexed object rather than the array of objects most of the time, it's a good option in terms of ease of use and performance.
And this is doing it with reduce and reduced again, using a mix of Ramda and vanilla JS to get the safety of Ramda's propEq with the speed advantage of a ternary operator over an R.ifElse, if performance is important: https://goo.gl/vNRy1M
Ben Briggs
@ben-eb
Oct 29 2018 20:17
@kane77 Another point-free version which is a bit easier to read :smiley:
const emptyOrEqU = either(
  compose(isNil, nthArg(0)),
  propEq('locationId')
)

const emptyOrEq = R.curryN(2, emptyOrEqU)
Ben Briggs
@ben-eb
Oct 29 2018 20:24
^ Although I might prefer to call it nilOrEq given that Ramda also has isEmpty
Kurt Milam
@kurtmilam
Oct 29 2018 20:42
@ben-eb, @kane77 I prefer an all-in-one solution like this, since you avoid the unnecessary filter operation completely if isNil(locationId):
const findMachines = ifElse(
  isNil,
  always(identity),
  o(filter, propEq('locationId'))
)
Ben Briggs
@ben-eb
Oct 29 2018 20:44
@kurtmilam Ah nice :smiley: I was working under the assumption that emptyOrEq was being used elsewhere
@kurtmilam But we could do this instead:
unless(isNil, o(filter, propEq('locationId')))
Kurt Milam
@kurtmilam
Oct 29 2018 20:46
@ben-eb have you tried it? :) I started down that path, originally, but it doesn't do the trick. If isNil(locationId), the function returns locationId, which it then calls with machines (error - locationId is not a function).
Ben Briggs
@ben-eb
Oct 29 2018 20:47
@kurtmilam Haha, I did and it doesn't work (but neither did ifElse)
You'd need to supply the other part to propEq
const x = l => unless(isNil, o(filter, propEq('locationId', l)))
Kurt Milam
@kurtmilam
Oct 29 2018 20:49
The ifElse example works if called like this: findMachines(locationId)(machines). I tested and confirmed that before posting. https://goo.gl/fqTX2Y
Ben Briggs
@ben-eb
Oct 29 2018 20:51
... because unless is unary. My bad, I'm used to making that conversion a lot with unary functions
Thanks :+1:
Kurt Milam
@kurtmilam
Oct 29 2018 20:53
No worries :) I tried with unless first (matching your suggestion, exactly), then realized what was happening.
This works whether called like findMachines(id)(machines) or findMachines(id, machines):
const findMachines = uncurryN(
  2,
  ifElse(
    isNil,
    always(identity),
    o(filter, propEq('id'))
  )
)
Ben Briggs
@ben-eb
Oct 29 2018 20:58
Yep
Riku Tiira
@rikutiira
Oct 29 2018 20:58
I have to say that these solutions are dangerously going in the code golf direction if you ask me :smile:
vs something like
const findMachines = R.curry((locationId, machines) => R.isNil(locationId) 
    ? machines 
    : machines.filter(R.propEq('locationId', locationId)))
Kurt Milam
@kurtmilam
Oct 29 2018 21:02
@rikutiira agreed :D. Was just about to post a solution basically identical to yours...
These days, I'd prefer the solution with the ternary operator to the one with ifElse in all cases.
Ben Briggs
@ben-eb
Oct 29 2018 21:03
Where's the fun in that? :wink:
Kurt Milam
@kurtmilam
Oct 29 2018 21:03
On the other hand ^^ :D
Lots of people enjoy playing golf...
Ben Briggs
@ben-eb
Oct 29 2018 21:05
I'm quite torn on ifElse vs ternary, I know that the built-in is much more performant but I really love the declarative style
Riku Tiira
@rikutiira
Oct 29 2018 21:07
ternary is also declarative, it’s basically a built-in ifElse in the language. In many cases I actually find Ramda’s ifElse lacking when you want to work with plain values vs functions
ternary is an expression just like Ramda’s ifElse afterall
But I get what you are saying, I also often like to write things in functions as it can feel cleaner :smile:
I guess by declarative you mean that it’s more descriptive
ifElse vs syntax you just have to learn to understand
Ben Briggs
@ben-eb
Oct 29 2018 21:08
Essentially yes

It does feel a bit wrong to use ifElse and always together, e.g.

const f = x => ifElse(equals(true), always(x), identity)

I kind of think it's best for the case when you wanted to test some predicate on a value and then run a function if true, or another if false

const f = x => y => equals(true, y) ? x : y
is much more readable
Ben Briggs
@ben-eb
Oct 29 2018 21:15

But if f is like this:

const f = ifElse(equals(true), success, error)

I think it's much more reasonable than

const f = x => equals(true, x) ? success(x) : error(x)
Riku Tiira
@rikutiira
Oct 29 2018 21:15
yep
I get why there wouldn’t be 2 versions of ifElse in Ramda, but it’d be nice to have one for functions and for plain values… naming could be a bit problematic though :smile: right now I have introduced separate ifElse which deals with plain values for my projects
if you had to pick one, then what Ramda has is definitely the way to go
Ben Briggs
@ben-eb
Oct 29 2018 21:18
const ifElse = (pred, a, b, z) => pred(z) ? a : b;
?
I guess there can be many variations of this function
Kurt Milam
@kurtmilam
Oct 29 2018 21:19
I agree - ifElse is fine if you have one input and want to choose between two different ways of processing the input based on the result of some predicate, but I don't think it's great in my solution above, where one branch is always(identity) :D
I also think it would be nice to have an ifElse function for plain values.
Riku Tiira
@rikutiira
Oct 29 2018 21:20
@ben-eb essentially yeah
Ben Briggs
@ben-eb
Oct 29 2018 21:26
Definitely agreed about always(identity) :D