James Flight
James Flight

Reputation: 1464

Ramda Js: Setting property on an object using a value from the same object

Using Ramda Js, I need to create a function that can set one object property using the value of a different property on the same object. My attempt so far is as follows:

var foo = R.set(R.lensProp('bar'), 'foo' + R.prop('foo'));
var result = foo({foo:"bar"});

Desired result:

{foo:"bar", bar:"foobar"}

Actual result:

{foo:"bar", bar: "foofunction f1(a) {... etc"}

Clearly I'm misunderstanding something here, and any insights into how to approach this would be appreciated.

Upvotes: 5

Views: 14001

Answers (3)

Scott Sauyet
Scott Sauyet

Reputation: 50807

I had just coded something like the answer from @davidchambers and then made a points-free version, only to show how much simpler the lambda actually is. Rather than throw it out, here's how bad it looks in comparison:

var foo = (obj) => R.assoc('bar', 'foo' + obj.foo, obj);
var foo = R.converge(R.assoc('bar'), [R.pipe(R.prop('foo'), R.concat('foo')), R.identity]);

These two, with an intermediate version are available on the Ramda REPL

Upvotes: 11

Lajos Mészáros
Lajos Mészáros

Reputation: 3866

I would slice the issue into 2 parts: first you need to copy the foo property of an object to bar, then change bar's value. There is no out of the box solution in ramda for the 1st, but you can use evolve for the second:

import { curry, assoc, compose, evolve } from 'ramda'

// String -> String -> {k: v}
const copyPropAs = curry((from, to, obj) => assoc(to, obj[from], obj))

// String -> String -> String
const prefix = curry((value, string) => value + string)

const fn = compose(
  evolve({
    foo: prefix('foo')
  }),
  copyPropAs('foo', 'bar')
)

fn({foo: 'bar'})

I know, that it's not all point free, but with this way the problematic part is isolated to a point where it can no longer be broken to smaller parts, and we can always come back to those to find better implementations.

Upvotes: 0

davidchambers
davidchambers

Reputation: 24856

Lenses are not a good fit when the value of one property depends on the value of another property. A lambda is probably best here:

const foo = o => R.assoc('bar', 'foo' + o.foo, o);

foo({foo: 'bar'});
// => {foo: 'bar', bar: 'foobar'}

Upvotes: 13

Related Questions