javascript - Ramda: How to change only one value of an object, based the object's other values - Stack Overflow

I'm building a React app. I'm using Ramda to help me with functional programming.If you want

I'm building a React app. I'm using Ramda to help me with functional programming.

If you want to see the full code, I also asked for help on StackExchange Code Review.

For Stack Overflow only one part is relevant, though.

How can you change a specific value from an object based on the object's other values using Ramda? I use mapObjIndexed which in the process maps over all keys.

Context: I have on object representing a contact with several keys which are all strings. The object always has a key called contactDetails. I want to calculate the value of contactDetails dependant of the values of the object's tel, bday and email keys.

If a contact looks like this before the function:

{
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '',
  tel: '555-55-5555',
  bday: '',
  email: '[email protected]'
}

it should look like this afterwards:

{
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '[email protected] 555-55-5555',
  tel: '555-55-5555',
  bday: '',
  email: '[email protected]'
}

The function that I wrote for this looks like this:

R.mapObjIndexed((val, key, obj) =>
  key === 'contactDetails'
    ? R.trim(
        R.replace(
          /undefined/g,
          '',
          `${R.prop('bday')(obj)} ${R.prop('tel')(obj)} ${R.prop('email')(
            obj
          )}`
        )
      )
    : val
),

As you can see this is a pretty unclean and hacky way abusing map. Is there a better way to change an object's value in Ramda based on other values of the object?

I'm building a React app. I'm using Ramda to help me with functional programming.

If you want to see the full code, I also asked for help on StackExchange Code Review.

For Stack Overflow only one part is relevant, though.

How can you change a specific value from an object based on the object's other values using Ramda? I use mapObjIndexed which in the process maps over all keys.

Context: I have on object representing a contact with several keys which are all strings. The object always has a key called contactDetails. I want to calculate the value of contactDetails dependant of the values of the object's tel, bday and email keys.

If a contact looks like this before the function:

{
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '',
  tel: '555-55-5555',
  bday: '',
  email: '[email protected]'
}

it should look like this afterwards:

{
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '[email protected] 555-55-5555',
  tel: '555-55-5555',
  bday: '',
  email: '[email protected]'
}

The function that I wrote for this looks like this:

R.mapObjIndexed((val, key, obj) =>
  key === 'contactDetails'
    ? R.trim(
        R.replace(
          /undefined/g,
          '',
          `${R.prop('bday')(obj)} ${R.prop('tel')(obj)} ${R.prop('email')(
            obj
          )}`
        )
      )
    : val
),

As you can see this is a pretty unclean and hacky way abusing map. Is there a better way to change an object's value in Ramda based on other values of the object?

Share Improve this question edited May 4, 2019 at 9:41 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Apr 25, 2019 at 8:37 J. HestersJ. Hesters 14.8k34 gold badges155 silver badges267 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 8

Unless this is an exercise in learning Ramda, I would suggest that a simpler technique than anything you're likely to e up with from Ramda is the straightforward object destructuring approach:

const transform = ({tel, bday, email, ...rest}) => ({
  ...rest, tel, bday, email,
  contactDetails: [bday, email, tel].join(' ').trim()
})

const obj = {firstName: 'John', lastName: 'Doe', tel: '555-55-5555', bday: '', email: '[email protected]'}

console.log(transform(obj))

This version does not depend upon the key contactDetails being already present, although it won't hurt if it is there.

If you're really worried about a possible double-space between words (for example, if bday and tel are supplied, but email is empty), you can modify it to this:

const bine = (ss) => ss.reduce((a, s) => a + (s.length ? ' ' : '') + s, '').trim()

const transform = ({tel, bday, email, ...rest}) => ({
  ...rest, tel, bday, email,
  contactDetails: bine([bday, email, tel])
})

I'm one of the founders of Ramda and a big fan, but it is only a toolkit. There are plenty of places where it helps make your code easier to read and to write; by all means use it then. But when it doesn't do so, even in a code-base heavily using Ramda, then skip it and use other techniques.

As an alternative to Ori's answer, we can update the property using R.assoc('contactDetails'), building a list of the desired property values using R.juxt and R.propOr('') to default any missing properties, rejecting any empty strings before joining them together with R.join.

// takes a list of property names, returning a function that accepts an object
// and produces a list of the values of the provided properties, defaulting to
// an empty string if null or undefined.
const defProps =
  R.pose(R.juxt, R.map(R.propOr('')))

const fn =
  // When `g` is a function, `R.chain(f, g)(x)` is equivalent to `f(g(x), x)`
  R.chain(
    R.assoc('contactDetails'),
    R.pipe(
      defProps(['bday', 'tel', 'email']),
      R.reject(R.equals('')),
      R.join(' ')))

console.log(fn({
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '',
  tel: '555-55-5555',
  bday: '',
  email: '[email protected]'
}))
<script src="https://cdnjs.cloudflare./ajax/libs/ramda/0.26.1/ramda.js"></script>

You can use R.converge to merge the original object, and the result of R.pipe that generates the contactDetails property. The pipe gets an array of values via R.props, filters out falsy values (undefined, empty strings, nulls, etc...), joins the the items with spaces, and wrap the value with an object with R.objOf.

const { converge, merge, identity, pipe, props, filter, join, trim, objOf } = R

const fn = converge(merge, [identity, pipe(
  props(['bday', 'tel', 'email']),
  filter(Boolean),
  join(' '),
  objOf('contactDetails')
)])

const obj = {
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '',
  tel: '555-55-5555',
  bday: '',
  email: '[email protected]'
}

const result = fn(obj)

console.log(result)
<script src="https://cdnjs.cloudflare./ajax/libs/ramda/0.26.1/ramda.js"></script>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1743957614a4536078.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信