【问题标题】:Ramda: How to change only one value of an object, based the object's other valuesRamda:如何根据对象的其他值仅更改对象的一个​​值
【发布时间】:2019-05-04 17:41:35
【问题描述】:

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

如果你想看完整的代码,我也在StackExchange Code Review寻求帮助。

不过,对于 Stack Overflow,只有一部分是相关的。

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

上下文:我有一个对象,它代表一个带有几个键的联系人,这些键都是字符串。该对象始终有一个名为contactDetails 的键。我想根据对象的 telbdayemail 键的值计算 contactDetails 的值。

如果一个联系人在函数之前是这样的:

{
  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 中更改对象的值?

【问题讨论】:

    标签: javascript functional-programming ramda.js


    【解决方案1】:

    除非这是学习 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 的代码库中,也可以跳过它并使用其他技术。

    【讨论】:

    • 一如既往地感谢斯科特。我喜欢你回答几乎所有用 Ramda 标记的问题的方式。它真的可以帮助人们学习??
    【解决方案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>

    【讨论】:

      【解决方案3】:

      您可以使用 R.converge 合并原始对象,以及生成 contactDetails 属性的 R.pipe 的结果。管道通过 R.props 获取一个值数组,过滤掉虚假值(未定义、空字符串、空值等),用空格连接项目,并用 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: '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>

      【讨论】:

        猜你喜欢
        • 2020-10-14
        • 2022-12-01
        • 2020-02-29
        • 1970-01-01
        • 2018-12-03
        • 2023-02-23
        • 2021-11-11
        • 1970-01-01
        • 2019-03-09
        相关资源
        最近更新 更多