These are chat archives for ramda/ramda

8th
Sep 2017
Greg Berns
@gregberns
Sep 08 2017 02:41 UTC

Question: Am trying to move my team from moving from for loops to map but someone brought up the need to exit a loop early if a condition is met.
So I asked the FP community on Slack about it and they suggested list.map().takeWhile() but in most FP it only solves the performance problem (exit early) if the list is lazy. So I implemented a mapWhile(mapFunction, predicate, functor). BUT, then looked at Ramda docs and saw takeWhile and saw the note about 'transducers'. I don't fully understand them yet, but was wondering if I could solve the problem somehow with it.
Maybe like: ??

var list = [1,2,3,4,5]
var times2 = function(i) { return i * 2; }
var lessThanFive = function(i) { return i < 5; }
//not sure how its structured...
R.takeWhile(lessThanFive, R.map(times2, list))

Would it be worth having a mapWhile(mapFunction, predicate, functor) function?
Just not sure how the transducer works, but seems like it might help with the problem.

Greg Berns
@gregberns
Sep 08 2017 02:48 UTC
Here's some code that does it... a bit rough still
gregberns/ramda@64c1323
Matthew Willhite
@miwillhite
Sep 08 2017 03:41 UTC

exit a loop early if a condition is met

@gregberns Have a look at reduced

Greg Berns
@gregberns
Sep 08 2017 06:28 UTC
@miwillhite dang, thats pretty nice. interesting the return isnt guaranteed. The only draw back I see is the transformation and reduction occur in the same function, not that its that big of a deal. Thanks for pointing that out
arian‮
@arian-swydo
Sep 08 2017 07:31 UTC
@gregberns just to give an example of plain javascript, you can break out of a reduce by emptying the array (the 4th argument to the callback): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce?v=a
Jonah
@jonahx
Sep 08 2017 08:40 UTC
is there a reason that R.range can’t accept decreasing sequences? eg R.range(4, 0) producing 4 3 2 1?
arian‮
@arian-swydo
Sep 08 2017 08:54 UTC
wouldn't it be the same as doing R.compose(R.reverse, R.range)
James Forbes
@JAForbes
Sep 08 2017 08:59 UTC
Yeah but you pay a perf cost
You can take advantage of times and do it all in loop R.times(R.subtract(4), 4 ) //=> [4, 3, 2, 1]
Jonah
@jonahx
Sep 08 2017 09:06 UTC
@arian-swydo yes, it would be a nice convenience though
@JAForbes right, but any reason not to have it work both ways? i mean, is there any downside? are we keeping to a philosophy in which we avoid any hint of “magic”? I can see an argument along those lines, since making it work both ways essential means there’s conditional logic of sorts in there — even if it’s very intuitive/expected. just sort of curious about the design philophy — or maybe I’m overthinking it and no one put it in yet because they didn’t care or think to?
Syaiful Bahri
@syaiful6
Sep 08 2017 09:09 UTC
I don't know about Ramda reason. There might be situations where you want a range [x..y] where y < x and where you expect the range to be empty.
So if the function didn't accept step argument, i would expect this function would not doing something smart, eg automatically step downward if y < x.
Jonah
@jonahx
Sep 08 2017 09:09 UTC
@syaiful6 yeah i could see that
Jonah
@jonahx
Sep 08 2017 09:17 UTC

Totally separate question. I was solving the problem of printing an ascii diamond like this:

   **
****
******
********
******
****
**

using a point-free style. Here’s my solution (https://goo.gl/F3sdcr):

const diamond = pipe(
range(0),
converge(zipWith(concat),
[pipe(map(repeatChar(' ')), reverse), map(repeatChar('*'))]),
converge(zipWith(concat), [identity, map(reverse)]),
converge(concat, [identity, pipe(reverse, tail)]),
join('\n')
)

Can this be simplified?

Kurt Milam
@kurtmilam
Sep 08 2017 11:37 UTC
@jonahx here's my go at it: ( https://goo.gl/zxgX8f )
I'm not sure whether it's simplified, but I only map once and was able to extract a reusable function out of it that I named mirrorPivot.
mirrorPivot works like this:
mirrorPivot( [ 1, 2, 3 ] ) //-> [ 1, 2, 3, 2, 1 ]
Kurt Milam
@kurtmilam
Sep 08 2017 11:47 UTC
Here's a minor edit where I replaced converge with juxt and apply.
Kurt Milam
@kurtmilam
Sep 08 2017 12:02 UTC
Here's an improvement. I've extracted a reusable mirror function that works like this:
mirror( [ 1, 2, 3 ] ) //-> [ 1, 2, 3, 3, 2, 1 ]
Stefano Vozza
@svozza
Sep 08 2017 12:15 UTC
very nice
Kurt Milam
@kurtmilam
Sep 08 2017 12:28 UTC
:+1: Thanks!
Kurt Milam
@kurtmilam
Sep 08 2017 12:48 UTC
And here's probably my last edit. I extracted an evolveArray function that works like this:
evolveArray( [ add( 1 ), add( 2 ), add( 3 ) ] )
( [ 1, 2, 3 ] )
//->[ 2, 4, 6 ]
Kurt Milam
@kurtmilam
Sep 08 2017 13:09 UTC
minor cleanup -- I simplified evolveArray and use o rather than pipe in the mirror* functions.
Kurt Milam
@kurtmilam
Sep 08 2017 13:39 UTC

You can also move mirrorPivot after the map in the main pipe, and everything still works as it should. It's possible that this would offer a performance improvement.

I'd like to thank you for supplying this interesting excuse to procrastinate on preparing my 2016 tax return :D

Rupesh Tiwari
@roopkt
Sep 08 2017 13:43 UTC
Why index of -1 is updating the last element of the array ?
R.adjust(R.add(10), -1, [1, 2, 3]);
//output [1, 2, 13]
https://goo.gl/yTmM6X
Kurt Milam
@kurtmilam
Sep 08 2017 13:44 UTC
@roopkt there are several functions that operate on arrays and treat negative indexes as instructions to start from the end of the array and work backwards.
This similar to the way the native Array.prototype.slice method treats negative indexes.
Stefano Vozza
@svozza
Sep 08 2017 13:56 UTC
python works that way too
Kurt Milam
@kurtmilam
Sep 08 2017 13:57 UTC
:+1: Ruby as well, I believe.
Rupesh Tiwari
@roopkt
Sep 08 2017 14:01 UTC
@kurtmilam @svozza Thanks for the explanation ! I learned this today ! However, I think if I need to update an array by finding keys and only that which matches the key then I can not use adjust function.
Kurt Milam
@kurtmilam
Sep 08 2017 14:02 UTC
I would say this should encourage you to write more robust code.
Calling adjust with an index that you don't expect to exist when you want a noop is not really good fp practice.
Rupesh Tiwari
@roopkt
Sep 08 2017 14:03 UTC
I am playing this example https://goo.gl/8Qo6Kx
const s = [ {visible:false, id:1} , {visible:false, id:2} ];
const ids = [1,4];
const findItemindex = (id) => findIndex(s=>s.id===id)(s)
const makeVisible = evolve({visible:T});
const updateArray =(s, id)=> adjust(makeVisible, findItemindex(id), s) ;
reduce(updateArray,s,ids)

// output -   [{"id": 1, "visible": true}, {"id": 2, "visible": true}]
// expected output - [{"id": 1, "visible": true}, {"id": 2, "visible": false}]
https://goo.gl/hthpmq
Kurt Milam
@kurtmilam
Sep 08 2017 14:15 UTC
@roopkt For instance
const s = [ { visible:false, id: 1 } , { visible:false, id: 2 } ]
const ids = [ 1, 4 ]
const setToVisible =
toVisible =>
map( when( o( flip( contains )( toVisible ) )
( prop( 'id' ) )
)
( assoc( 'visible')( true ) )
)
setToVisible( ids )( s )
//-> [{"id": 1, "visible": true}, {"id": 2, "visible": false}]
Kurt Milam
@kurtmilam
Sep 08 2017 14:24 UTC
That maps through everything ins. It may make sense to map ids, instead. I'll see what I can do.
Rupesh Tiwari
@roopkt
Sep 08 2017 14:38 UTC
@kurtmilam I opted this option first but map goes through each loop
Can we do something in adjust that if index -1 then do not do any thing ?
Kurt Milam
@kurtmilam
Sep 08 2017 14:54 UTC
@roopkt well, you could write a strictAdjust wrapper for adjust that noops when supplied with an index that is not a positive integer. Something like this:
const strictAdjust =
fn => idx => x =>
ifElse( gte( 0 ) )
( _ => adjust( fn )( idx )( s ) )
( _ => s )
( idx )
Kurt Milam
@kurtmilam
Sep 08 2017 15:08 UTC
@roopkt Here's an example using strictAdjust and a better findItemIndex.
I also wouldn't use evolve here. assoc is better (I replaced that in the linked example).
Stefano Vozza
@svozza
Sep 08 2017 15:13 UTC
i think you need to do your tax return now. :P
Kurt Milam
@kurtmilam
Sep 08 2017 15:20 UTC
Yes, I really do :D
@roopkt there are many ways to do this. Another would be to sanitize the list of ids so that they only contain ids that will be found in your s. It would be interesting to know why ids can contain ids that aren't in s, as well. Mutable state?
Sep 08 2017 16:12 UTC

@jonahx

is there a reason that R.range can’t accept decreasing sequences? eg R.range(4, 0) producing 4 3 2 1?

There has been a bit of discussion around that exact subject:
ramda/ramda#702
ramda/ramda#2109
http://fr.umio.us/ranging-near-and-far/

Jonah
@jonahx
Sep 08 2017 16:21 UTC
@kurtmilam thanks. didn’t know about o. also like pulling the mirror action out. not crazy about the formatting but that’s an aside/nitpick
Kurt Milam
@kurtmilam
Sep 08 2017 16:43 UTC
@jonahx I've been evangelizing for that code style for some time, and I guess that at least some parts of it are likely to become more popular in the future. sanctuary-js/sanctuary#438.
Also relevant: the npm team's style guide.
Darren
@dardub
Sep 08 2017 17:55 UTC
I'm trying to compose a function, and I want to put the subject into another object. I'm not clear how to do that. (Newbie here). Hopefully this explains it. Is this the way to do it? I was trying R.assoc, but the parameters would be in the wrong palce.
 o(
(a) => { query: a},
function1,
function2
)( { foo: 1, bar: 2 })
Sep 08 2017 17:56 UTC
R.objOf
ram-bot
@ram-bot
Sep 08 2017 17:56 UTC
Darren
@dardub
Sep 08 2017 17:57 UTC
Wow. thanks. didn't expect such a specific function. :thumbsup:
Sep 08 2017 17:57 UTC
@ram-bot
o(objOf('query'), prop('foo'))({foo: 1, bar: 2})
ram-bot
@ram-bot
Sep 08 2017 17:57 UTC
o is not defined
Sep 08 2017 17:58 UTC
@ram-bot
compose(objOf('query'), prop('foo'))({foo: 1, bar: 2})
ram-bot
@ram-bot
Sep 08 2017 17:58 UTC
{ query: 1 }
Sep 08 2017 17:58 UTC
@dardub Keep in mind that o will only work with 2 functions, and they both have to be unary. compose and pipe work with more functions
Darren
@dardub
Sep 08 2017 18:00 UTC
Ok. Thanks. I need to read up on the difference more.
Between pipe and compose, is that just style preference?
Do people sometimes use one or the other in a project, or just stick to one?
Sep 08 2017 18:01 UTC
Yes. depending on the situation, one or the other might be more readable
Personally, I use compose if it's all on the same line, and pipe if I want to break it up over multiple lines
Darren
@dardub
Sep 08 2017 18:02 UTC
Ah interesting. I can see that.
Sep 08 2017 18:02 UTC
You will find there are many different opinions on the subject though
Darren
@dardub
Sep 08 2017 18:02 UTC
I bet!
Michael Rosata
@mrosata
Sep 08 2017 18:03 UTC
Personally, I use compose if it's all on the same line, and pipe if I want to break it up over multiple lines
Jonah
@jonahx
Sep 08 2017 18:08 UTC
@kurtmilam maybe i’ll have to think more deeply about it. my gut reaction is that while i see it has a more consistent structure it’s too verbose for my taste. i like to be near the limit of “comfortable density”. i think prettier does a good job of this for js
Darren
@dardub
Sep 08 2017 18:08 UTC
Is my understanding of toString that you can use it on an object just like JSON.stringify correct?
Kurt Milam
@kurtmilam
Sep 08 2017 18:10 UTC
Too many linebreaks and spaces?
Jonah
@jonahx
Sep 08 2017 18:10 UTC
yeah
and parens
Sep 08 2017 18:12 UTC
@dardub yes. The ideal (while not 100% correct) is that you should be able to do equals(o, eval(toString(o))
Kurt Milam
@kurtmilam
Sep 08 2017 18:12 UTC
Gotcha. I say I 'evangelize' for that style, but that's not really true. I write examples in that style, but I won't try to argue with anyone that it's the best or a better style.
I do like the parens, because they make the fact the you're calling curried functions more clear.
Darren
@dardub
Sep 08 2017 18:13 UTC
Jonah
@jonahx
Sep 08 2017 18:16 UTC
@kurtmilam sure. and it’s all bikeshedding of sorts. i could get used to that style in about 45mins and be perfectly happy.
Kurt Milam
@kurtmilam
Sep 08 2017 18:17 UTC
:+1: I don't like having to look at a section of code that ends in something like ))}])]})))]}) and try to figure out what belongs where :)
Your example was nowhere near that nested, but I've come across (and written) plenty that are complicated to work through.
I also like to keep my lines as short as possible (horizontally). Maybe I have an easier time reading it that way because I'm an old :D
Jonah
@jonahx
Sep 08 2017 18:39 UTC
@kurtmilam funny how much funnier that word is with the indefinite article. i too am an old, relatively speaking, but i solve it by ⌘ + my font and keep my information density. my 24 yo co-worker created a hotkey so when i went to help him at his desk he could quickly switch out of his pinpoint font into a jonah-friendly size.