Ramda:如何仅根据对象的其他值更改其一个值

时间:2019-04-25 08:37:26

标签: javascript functional-programming ramda.js

我正在构建一个React应用。我正在使用Ramda来帮助我进行函数式编程。

如果您想查看完整的代码,我还请求StackExchange Code Review上的帮助。

对于堆栈溢出,只有一部分是相关的。

如何使用Ramda根据对象的其他值更改对象的特定值?我使用mapObjIndexed,它在过程中会映射所有键。

上下文:我在一个对象上用几个都是字符串的键表示一个联系人。该对象始终具有一个称为contactDetails的键。我想根据对象的contactDetailstelbday键的值来计算email的值。

如果函数前的联系人如下:

{
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: '',
  tel: '555-55-5555',
  bday: '',
  email: 'john@doe.com'
}

之后应该是这样的:

{
  firstName: 'John',
  lastName: 'Doe',
  contactDetails: 'john@doe.com 555-55-5555',
  tel: '555-55-5555',
  bday: '',
  email: 'john@doe.com'
}

我为此编写的函数如下:

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
),

如您所见,这是滥用map的一种非常不干净和骇人听闻的方式。是否有更好的方法可以根据对象的其他值在Ramda中更改对象的值?

3 个答案:

答案 0 :(得分:3)

除非这是学习Ramda的练习,否则我建议,比您可能会从Ramda想到的任何方法更简单的技术是简单的对象分解方法:

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: 'john@doe.com'}

console.log(transform(obj))

此版本不依赖于已存在的密钥contactDetails,尽管它在那里也不会受到损害。

如果您真的担心单词之间可能存在双倍空格(例如,如果提供了bdaytel,但email为空),则可以对其进行修改对此:

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

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

我是Ramda的创始人之一,也是忠实的粉丝,但这只是一个工具包。在很多地方,它可以使您的代码更易于阅读和编写。然后一定要使用它。但是,如果不这样做,即使在大量使用Ramda的代码库中,也可以跳过它并使用其他技术。

答案 1 :(得分:2)

作为Ori答案的替代方法,我们可以使用R.assoc('contactDetails')更新属性,使用R.juxtR.propOr('')建立所需属性值的列表,以默认缺少的属性,拒绝任何遗漏的属性空字符串,然后将它们与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.compose(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: 'john@doe.com'
}))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

答案 2 :(得分:1)

您可以使用R.converge合并原始对象,并使用R.pipe的结果生成contactDetails属性。管道通过R.props获取值数组,过滤掉虚假值(未定义,空字符串,空值等),将项目与空格连接在一起,并使用R.objOf。< / p>

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: 'john@doe.com'
}

const result = fn(obj)

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