最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin5浏览0评论

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>

发布评论

评论列表(0)

  1. 暂无评论