These are chat archives for ramda/ramda

21st
Apr 2016
Emilio Srougo
@Emilios1995
Apr 21 2016 00:37
Hi people! I looking for a way to use merge(), but I need to modify the keys before applying them to the destination. for example, my source is {a: 1, b: 2} and I want to merge it with an empty object, but with the result looking like this {_a: 1, _b:2}
any suggestions?
David Langford
@That-David-Guy
Apr 21 2016 00:53
@ram-bot
const data1 = {a: 1, b: 2} 
const data2 = {c: 3}
const mergedObj = R.merge(data1, data2)
const updateKeys = obj => R.map(x => '_'+x, R.keys(obj))
R.zipObj(updateKeys(mergedObj), R.values(mergedObj))
ram-bot
@ram-bot
Apr 21 2016 00:53
{ _a: 1, _b: 2, _c: 3 }
David Langford
@That-David-Guy
Apr 21 2016 00:53
That is a start, I think I can make it a bit more readable though
@xgrommx That looks useful, but he would still have to get the value for each key. Which I would think where the difficulty is
David Langford
@That-David-Guy
Apr 21 2016 01:02
@ram-bot
const updateKeys = o => R.map(x => '_'+x, R.keys(o))
const zipUpdatedKeys = o => R.zipObj(updateKeys(o), R.values(o))
const mergeAndUpdateKeys = R.compose(zipUpdatedKeys, R.merge);

const data1 = {a: 1, b: 2} 
const data2 = {c: 3}
mergeAndUpdateKeys(data1, data2)
ram-bot
@ram-bot
Apr 21 2016 01:02
{ _a: 1, _b: 2, _c: 3 }
David Langford
@That-David-Guy
Apr 21 2016 01:03
Is that what you were looking for @Emilios1995 ? There might be an even more concise way to do it
Emilio Srougo
@Emilios1995
Apr 21 2016 01:10
@That-David-Guy yes, thanks! I didn't know zipObject. How would you do it more concisely?
Baqer Mamouri
@bmamouri
Apr 21 2016 01:11

Hey folks, I wrote a small function that find the minimum latitude from a list:

const findPlacesBoundingBox = 
R.pipe(
  R.map(
    R.pipe(
      R.path(['googlePlaceObj', 'geometry', 'location', 'lat']),
    ),
  ),
  R.reduce(R.min, Infinity),
)

I also wants to find the maximum latitude. I can repeat the function for the maximum but it is not DRY and I am sure there should be a better way in Ramda.

Also it is not very efficient. How do you do this in one go?
And do we have a function that do exactly the opposite of zipObj? toPairs is not exactly the opposite of zipObj
Baqer Mamouri
@bmamouri
Apr 21 2016 01:33
Okay I think I found the answer to my second questions. I needed R.values function to get rid of the keys
David Langford
@That-David-Guy
Apr 21 2016 01:38
@Emilios1995 Not sure. I'm still new to functional programming so there is usually something I'm missing :)
Emilio Srougo
@Emilios1995
Apr 21 2016 01:51
@That-David-Guy nevermind, that was just what I needed! :smile:
Scott Christopher
@scott-christopher
Apr 21 2016 02:01
@bmamouri It's perhaps not the nicest implementation, but you could do something like the following:
@ram-bot
const fn = R.pipe(
  R.map(R.path(['googlePlaceObj', 'geometry', 'location', 'lat'])),
  R.apply(R.applySpec({ min: Math.min, max: Math.max }))
)

const input = [
  { googlePlaceObj: { geometry: { location: { lat: 0 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 5 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 2 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 3 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 6 }}}}
]

fn(input)
ram-bot
@ram-bot
Apr 21 2016 02:02
{ min: 0, max: 6 }
Baqer Mamouri
@bmamouri
Apr 21 2016 02:05
@scott-christopher Thank you. apply and applySpec was the missing ingredient. That exactly does what I want.
Now if I want to also do this for longtitude I should have two different functions. Something like this:
const getMinMax(keyName) =>  R.pipe(
  R.map(R.path(['googlePlaceObj', 'geometry', 'location', keyName])),
  R.apply(R.applySpec({ min: Math.min, max: Math.max }))
)
Or there is a better alternative?
I actually think what I want to do is a bit different. Here is what I achieved so far:
const places = [
    {
        googlePlaceObj: {
            geometry: {
                location: {
                    lat: 1,
                    lng: 2,
                }
            }
        }
    },
    {
        googlePlaceObj: {
            geometry: {
                location: {
                    lat: 11,
                    lng: 22,
                }
            }
        }
    }
]

const getAllLocations = 
R.pipe(
    R.map(
      R.pipe(
         R.path(['googlePlaceObj', 'geometry', 'location']),
         R.values
      ),
    ),
)
This will result in a pairs of [lat, lng]s, like so: [[1, 2], [11, 22]]
Baqer Mamouri
@bmamouri
Apr 21 2016 02:10
Now I want to get the minimum lat and lng from the array and maximum lat and lng from the array.
To create a bounding rectangle from all the locations.
If I could just convert the array of [[lat, lng], [lat, lng], [lat, lng], …] into [[lat, lat, lat …], [lng, lng, lng, …]] I could easily use R.minBy and R.maxBy to find the boundary.
Scott Christopher
@scott-christopher
Apr 21 2016 03:20
R.transpose
ram-bot
@ram-bot
Apr 21 2016 03:20
[R.transpose ∷ [[a]] → [[a]]](http://ramdajs.com/docs/#transpose)
Scott Christopher
@scott-christopher
Apr 21 2016 03:21
@bmamouri transpose can achieve that.
@ram-bot
R.transpose([[1, 'a'], [2, 'b'], [3, 'c']])
ram-bot
@ram-bot
Apr 21 2016 03:21
[ [ 1, 2, 3 ], [ 'a', 'b', 'c' ] ]
Baqer Mamouri
@bmamouri
Apr 21 2016 03:38
Thank y ou @scott-christopher for helping me out. R.transpose was exactly what I was looking for.
Scott Christopher
@scott-christopher
Apr 21 2016 03:38
You're welcome :)
Baqer Mamouri
@bmamouri
Apr 21 2016 03:39

Last question and thank you in advance.

How can I reuse this code for finding the bootomRight bounds. The only differet is that R.minshould be replaced with R.max:

const getTopLeft =
R.pipe(
    R.map(
      R.pipe(
         R.path(['googlePlaceObj', 'geometry', 'location']),
         R.values,
      ),
    ),
    R.transpose,
    R.map(R.reduce(R.min, Infinity)),
)

getTopLeft(places)
I can make the function to get parameter, but I am thinking maybe there is a cleaner way.
Scott Christopher
@scott-christopher
Apr 21 2016 03:44
It's starting to sound like you might benefit from modeling this using a separate data type.
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 03:49
@bmamouri In your inner pipe up there you might consider using props instead of values as values doesn't guarantee the order is preserved.
props
R.props
Baqer Mamouri
@bmamouri
Apr 21 2016 03:58
Good point @Bradcomp. Thank you. I did not notice that the order is not preserved
Scott Christopher
@scott-christopher
Apr 21 2016 03:59
If you create a monoid instance for a bounding box, you should be able to do something like:
This message was deleted
ram-bot
@ram-bot
Apr 21 2016 03:59
SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Scott Christopher
@scott-christopher
Apr 21 2016 04:00
hmm, let me try that again...
@ram-bot
class BBox {
  constructor(tlLat, tlLng, brLat, brLng) {
    this.tlLat = tlLat;
    this.tlLng = tlLng;
    this.brLat = brLat;
    this.brLng = brLng;
  }

  concat(other) {
    var tlLat = max(this.tlLat, other.tlLat),
        tlLng = min(this.tlLng, other.tlLng),
        brLat = min(this.brLat, other.brLat),
        brLng = max(this.brLng, other.brLng);
    return new BBox(tlLat, tlLng, brLat, brLng);
  }

  static fromLatLng({lat, lng}) {
    return new BBox(lat, lng, lat, lng)
  }

  static empty()
  {
    return new BBox(-Infinity, Infinity, Infinity, -Infinity)
  }
}

var objToBBox = pipe(path(['googlePlaceObj', 'geometry', 'location']), BBox.fromLatLng)

var fn = reduce(useWith(concat, [identity, objToBBox]), BBox.empty());

fn([
  { googlePlaceObj: { geometry: { location: { lat: 0, lng: 2 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 5, lng: 5 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 2, lng: 6 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 3, lng: 7 }}}},
  { googlePlaceObj: { geometry: { location: { lat: 6, lng: 3 }}}}
]);
ram-bot
@ram-bot
Apr 21 2016 04:00
SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Scott Christopher
@scott-christopher
Apr 21 2016 04:00
hmm, maybe it doesn't like the class statement
anyway, that evaluates to
{"brLat": 0, "brLng": 7, "tlLat": 6, "tlLng": 2}
The empty instance is a bit of a stretch of the imagination, but it works for this purpose.
Baqer Mamouri
@bmamouri
Apr 21 2016 04:05
This is a beuatiful piece of code @scott-christopher. Being a noob in FP I am not familiar enough with Monoids. But I can see how this works.
Scott Christopher
@scott-christopher
Apr 21 2016 04:06
A monoid is something with a fancy name that is actually pretty straight forward :)
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 04:06
@scott-christopher TBH, your many code snippets here have helped me be more comfortable with them as well.
@bmamouri I went a lot more literal with it: http://goo.gl/NMjQVD
Scott Christopher
@scott-christopher
Apr 21 2016 04:07
It basically means you have some function that can take two instances of some type and return an instance of the same type.
Along with a few laws around associativity and identity that need to be adhered to.
e.g. concat(a, concat(b, c)) must be equal to concat(concat(a, b), c)
and concat(MonoidThing.empty(), monoidThingInstance) must be equal to monoidThingInstance
and the flipped version of that:
concat(monoidThingInstance, MonoidThing.empty()) must equal monoidThingInstance
Some intuitive ones are numbers with addition.
add(1, add(2, 3)) == add(add(1, 2), 3)
add(0, 5) == 5 add(5, 0) == 5
along with numbers and multiplication, arrays, strings, etc.
Baqer Mamouri
@bmamouri
Apr 21 2016 04:13
I did something like you did as well @Bradcomp.
Hardy Jones
@joneshf
Apr 21 2016 04:15
@Bradcomp you going to the next sacjs?
elm!
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 04:18
@joneshf I know! I'm gonna try to be there!
David Langford
@That-David-Guy
Apr 21 2016 04:19
@Bradcomp With regards to values not having a guarantee that order is preserved. If it is not, is it consistent with keys. I.e. are they both inconsistent in the same way
As in, would this fail
@ram-bot
const updateKeys = o => R.map(x => '_'+x, R.keys(o))
const zipUpdatedKeys = o => R.zipObj(updateKeys(o), R.values(o))
const mergeAndUpdateKeys = R.compose(zipUpdatedKeys, R.merge);

const data1 = {a: 1, b: 2} 
const data2 = {c: 3}
mergeAndUpdateKeys(data1, data2)
ram-bot
@ram-bot
Apr 21 2016 04:19
{ _a: 1, _b: 2, _c: 3 }
David Langford
@That-David-Guy
Apr 21 2016 04:20
Say keys go a->c->b order, will values also be a->c->b order?
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 04:22
@That-David-Guy It's up to the Javascript implementation I believe, so I wouldn't count on that, though it's probably fine.
I would probably go for toPairs and fromPairs
David Langford
@That-David-Guy
Apr 21 2016 04:23
@Bradcomp Thanks! Didn't know about toPairs
David Langford
@That-David-Guy
Apr 21 2016 04:30
@Emilios1995 . There was a bug with the code I gave you since values and keys don't guarantee order. This should be more safe
@ram-bot
const data1 = {a: 1, b: 2} 
const data2 = {c: 3}

const updateKey = R.map(x=>['_'+x[0], x[1]] )
const mergeAndUpdateKeys2 = 
  R.compose(R.fromPairs, updateKey, R.toPairs, R.merge)

mergeAndUpdateKeys2(data1, data2)
ram-bot
@ram-bot
Apr 21 2016 04:30
{ _a: 1, _b: 2, _c: 3 }
David Langford
@That-David-Guy
Apr 21 2016 04:30
There is probably still a better way to do the updateKey part.
Emilio Srougo
@Emilios1995
Apr 21 2016 04:34
@That-David-Guy Thanks! although the former version seemed more intuitive to me. are you sure it's not safe? I did a couple of tests and it worked good
*well
Oh I just read @Bradcomp's messages
David Langford
@That-David-Guy
Apr 21 2016 04:39
:)
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 04:44
Like I said it's probably okay, there's just no guarantee of order
Looking under the hood at R.values, maybe it is safe: https://github.com/ramda/ramda/blob/v0.21.0/src/values.js
as they use keys to get their key array
Emilio Srougo
@Emilios1995
Apr 21 2016 04:48
I see. But, why would the JS make a difference?
*JS platform
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 04:52
The ECMAScript spec doesn't require an ordering for Object.keys so it's up to the implementation to decide how to order
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 06:04
@That-David-Guy @Emilios1995 In case you guys were interested, here's a lens based solution: http://goo.gl/hzRJdk
David Langford
@That-David-Guy
Apr 21 2016 06:22
Thanks @Bradcomp ! I haven't really grokked lens and over yet so I find that one harder to read. I'll have to mull over it tonight
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 06:23
This was mainly an exercise to help me understand them better
David Langford
@That-David-Guy
Apr 21 2016 06:31
:)
Emilio Srougo
@Emilios1995
Apr 21 2016 06:34
@That-David-Guy @Bradcomp Lenses are new to me as well, this is definitely an interesting solution!
John-David Dalton
@jdalton
Apr 21 2016 06:36
@Bradcomp There is a specified order (ES6) for Object.keys and own property iteration.
Valery
@iofjuupasli
Apr 21 2016 10:24

Hi, guys. What do you think about using Lenses for converting values between two types? Milliseconds <-> Seconds (just two number types, but semantically different). What is nice about Lenses is that they are composable. So I can compose ms <-> seconds, and seconds <-> minutes. If Lens is not just for focusing on part of bigger thing, but for "looking on thing from different angle", then it seems nice usage for them. So, I have ms, but with Lens, I work with it like I have seconds. What I want is in fact pair of function and inverse function (a -> b, b -> a), but in composable structure. When I use Lens for that, it works, but I should ignore "previous real thing" in setter, and when use .set, should pass anything (which seems purposelessly).

const secondsLens = R.lens(v => v / 1000, v => _ => v * 1000);
const twoMinutes = R.set(secondsLens, 120, null);

So is there exist type for such pair a -> b, b -> a. Or should I use Lenses for that?

Scott Sauyet
@CrossEye
Apr 21 2016 11:40
"A monoid is something with a fancy name that is actually pretty straight forward." Absolutely. I think that one day I'll be smart enough that Monads seem that way too. :smile:
@iofjuupasli: I think that converting between related types/units is a great use of lenses. I'm not following the "pre ious real thing" objection.
Scott Christopher
@scott-christopher
Apr 21 2016 12:24
@iofjuupasli: you could also declare these with an iso that can compose with other lenses.
@ram-bot
const iso = (to, fro) => afb => s => map(fro, afb(to(s)))

const secsToMins  = iso(flip(divide)(60), multiply(60))
const milliToSecs = iso(flip(divide)(1000), multiply(1000))

const durationToMins = compose(lensProp('duration'), milliToSecs, secsToMins)

over(durationToMins, add(2), { duration: 0 })
ram-bot
@ram-bot
Apr 21 2016 12:24
{ duration: 120000 }
Valery
@iofjuupasli
Apr 21 2016 12:37

@CrossEye when I "set" new value, it replaces old, and returns it. In example, I've provided, _ in v => _ => v * 1000 and null in set. I doubt whether it's ok, or am I trying to apply lenses in wrong place. At first glance, it seemed to me, that it would be better to create or find another type for composing function with its inversed version.

@scott-christopher awesome! I don't understand how iso works, just as I don't understand how lenses work in general, so I should learn about it first.

Scott Christopher
@scott-christopher
Apr 21 2016 12:41
Some type sigs for the various arguments might help.
// to  :: s -> a
// fro :: a -> s
// afb :: Functor f => a -> f a
iso = (to, fro) => afb => s => map(fro, afb(to(s)))
afb is the function given by over / view that puts the focussed value (the result of to(s)) inside a functor (Identity for over, Const for view)
to and fro are the functions used convert between two types (s and a in the above).
Hardy Jones
@joneshf
Apr 21 2016 13:32
@iofjuupasli Your idea is an excellent use of lenses (or isos as @scott-christopher points out)!
Valery
@iofjuupasli
Apr 21 2016 13:33
Just found https://github.com/ramda/ramda-lens/blob/master/test%2Ftest.js#L120-L134 and https://github.com/ramda/ramda-lens/blob/master/src%2Fisos.js#L5-L13. With that I can simply switch lens a -> b, b -> a -> a to lens b -> a, a -> b -> b, and also use only .view method. I found using .set for getting value back from "lensed" value is very confusing. R.view(from(minsLens)) is more obvious
Risto Stevcev
@Risto-Stevcev
Apr 21 2016 15:18
Is there something like R.splitWhen except it will split the string on every matching predicate? I'm looking for something higher level than reduce
something like this: R.splitWhen(';', ['foo', 'bar', ';', 'baz', ';', 'qux', 'norf', ';']) // [['foo', 'bar'], ['baz'], ['qux', 'norf']]
Risto Stevcev
@Risto-Stevcev
Apr 21 2016 15:43
I feel like there should be a split for lists, but there doesn't seem to be one
David Chambers
@davidchambers
Apr 21 2016 16:22
@ram-bot
R.splitWhen(R.equals(';'), ['foo', 'bar', ';', 'baz', ';', 'qux', 'norf', ';'])
ram-bot
@ram-bot
Apr 21 2016 16:22
[ [ 'foo', 'bar' ], [ ';', 'baz', ';', 'qux', 'norf', ';' ] ]
David Chambers
@davidchambers
Apr 21 2016 16:22
Not quite!
Emilio Srougo
@Emilios1995
Apr 21 2016 16:55
@jdalton So, returning to my issue, does Object.keys guarantee accurate order?
if so, the warning in the docs for https://github.com/ramda/ramda/blob/v0.21.0/src/values.js would need to be removed
Keith Alexander
@kwijibo
Apr 21 2016 17:02
does R.assoc mess with prototype stuff?
i found the .constructor of the result is function Object instead of the original constructor
function Foo(){}
const f = new Foo()
const g = R.assoc('k', 42, f)
g instanceof Foo //false
f instanceof Foo //true
hmm, R.set also messes it up :(
John-David Dalton
@jdalton
Apr 21 2016 17:31
@Emilios1995 For ES6 yes for earlier it's de facto.
Emilio Srougo
@Emilios1995
Apr 21 2016 18:01
Ok, thanks.
One more question, as you may have seen I'm to to all this and I really liked the comments you guys put before every function (Hindley-Milner). Now, I made a function to help with what I was trying to do (update object keys)
const headLens = lensIndex(0)
const updateKeys = (f) => 
  R.compose(R.fromPairs, 
            R.map(over(headLens, f)), 
            R.toPairs)
updateKeys(updateKey)(obj)
How would the type annotation comment would look like for updateKeys ?
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 18:08
Something like Object -> (String -> String) -> Object
or { String : * } -> (String -> String) -> { String: * }
Emilio Srougo
@Emilios1995
Apr 21 2016 18:09
Oh I got it. Thanks!
Brad Compton (he/him)
@Bradcomp
Apr 21 2016 18:17
I had that backwards though
(String -> String) -> { String : * } -> { String: * }
Emilio Srougo
@Emilios1995
Apr 21 2016 18:57
oh I see. But I think I would just use Object, not { String : * }, because an object key will always be a string.