【问题标题】:Recursive "merge" or "extend" with Ramda?与 Ramda 递归“合并”或“扩展”?
【发布时间】:2016-07-01 00:49:27
【问题描述】:

我正在尝试使用Ramda 找到与Lodash's merge 等效的函数,该函数执行基于递归对象键的“合并”或“扩展”。行为类似于以下:

let merged = R.someMethod(
  { name: 'Matt', address: { street: 'Hawthorne', number: 22, suffix: 'Ave' }},
  { address: { street: 'Pine', number: 33 }}
);

console.log(merged);

// => { name: 'Matt', address: { street: 'Pine', number: 33, suffix: 'Ave' }}

我注意到在下面的pull request 中简要介绍了R.set,但随后不久就介绍了rolled back。从那以后,Ramda 库是否捕获了此功能?

Ramda 是否提供此功能?

【问题讨论】:

    标签: javascript functional-programming ramda.js


    【解决方案1】:

    使用R.mergeWith可以创建一个相对简单的递归函数。

    function deepMerge(a, b) {
      return (R.is(Object, a) && R.is(Object, b)) ? R.mergeWith(deepMerge, a, b) : b;
    }
    
    deepMerge({ name: 'Matt', address: { street: 'Hawthorne', number: 22, suffix: 'Ave' }},
              { address: { street: 'Pine', number: 33 }});
    
    //=> {"address": {"number": 33, "street": "Pine", "suffix": "Ave"}, "name": "Matt"}
    

    【讨论】:

    • 我想这取决于你希望这样一个函数如何工作,但我更喜欢作为第二个参数传递的任何键/值来覆盖作为第一个参数传递的匹配键/值,尽管值的类型。此类功能由(R.is(Object, b)) ? ... 而非(R.is(Object,a)) ?... 捕获。
    • 如果您可能在同一个键上处理不同的类型,那么您应该在调用mergeWith 之前检查ab 是否都是对象。我已经更新了示例以反映这一点。
    • 稍作修改的版本可以处理简单的数组const deepMerge = (a, b) => R.isArrayLike(b) && !R.is(Object, b[0]) ? b : (R.is(Object, a) && R.is(Object, b)) ? R.mergeWith(deepMerge, a, b) : b
    【解决方案2】:

    Ramda 目前不包含这样的功能。

    已经有过几次尝试来创建一个,但他们似乎在这样一个功能真正需要什么的概念上失败了。

    如果您认为值得添加,请随时raise an issue

    更新

    (两年后。)最终以几个函数的形式添加了它:mergeDeepLeftmergeDeepRightmergeDeepWithmergeDeepWithKey

    【讨论】:

    • 我认为值得创建一个问题,但我不想重新讨论关于如何处理未定义值、合并数组等的旧内容。我并没有真正提出任何细微的意见桌子,我不确定在这一点上拥有功能本身的愿望是否非常响亮......
    • 如你所愿,当然。我认为添加这样的功能存在持续的但相当低的压力。但包括我在内的许多人都反对,因为没有 API 看起来很棒,尤其是在原型链、undefined 和循环结构面前。但这可能是完美成为善的敌人的情况。
    【解决方案3】:

    Ramda 现在有几个合并函数:mergeDeepLeftmergeDeepRightmergeDeepWithmergeDeepWithKey

    【讨论】:

      【解决方案4】:

      const { unapply, mergeDeepRight, reduce } = R
      const mergeDeepRightAll = unapply(reduce(mergeDeepRight, {}))
      
      console.log(mergeDeepRightAll({a:1, b: {c: 1}},{a:2, d: {f: 2}},{a:3, b: {c:3}}))
      <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

      【讨论】:

        【解决方案5】:

        从头开始

        Ramba 库中的新功能意味着您不必自己执行此操作,但如果维护人员从不使用它怎么办?当您现在需要某项功能或行为时,您不希望等待其他人编写您的代码。

        下面,我们实现自己的递归merge

        const isObject = x =>
          Object (x) === x
        
        const merge = (left = {}, right = {}) =>
          Object.entries (right)
            .reduce
              ( (acc, [ k, v ]) =>
                  isObject (v) && isObject (left [k])
                    ? { ...acc, [k]: merge (left [k], v) }
                    : { ...acc, [k]: v }
              , left
              )
        

        我们的合并函数也可以通用并接受任何两个对象作为输入。

        const x =
          { a: 1, b: 1, c: 1 }
        
        const y =
          { b: 2, d: 2 }
        
        console.log (merge (x, y))
        // { a: 1, b: 2, c: 1, d: 2 }
        

        如果每个对象都包含一个属性,其值也是一个对象,merge 将递归并合并嵌套的对象。

        const x =
          { a: { b: { c: 1, d: 1 } } }
        
        const y =
          { a: { b: { c: 2, e: 2 } }, f: 2 }
        
        console.log (merge (x, y))
        // { a: { b: { c: 2, d: 1, e: 2 } }, f: 2 }
        

        数组也是人

        为了支持merge 中的数组,我们引入了一个突变助手mut,它将[ key, value ] 对分配给给定对象o。数组也被视为对象,因此我们可以使用相同的mut 函数来更新数组和对象

        注意,Ramda 的合并函数不会尝试合并数组。编写自己的函数的主要优点是您可以轻松地增强它们的行为以满足程序不断发展的需求。

        const mut = (o, [ k, v ]) =>
          (o [k] = v, o)
        
        const merge = (left = {}, right = {}) =>
          Object.entries (right)
            .map
              ( ([ k, v ]) =>
                  isObject (v) && isObject (left [k])
                    ? [ k, merge (left [k], v) ]
                    : [ k, v ]
              )
            .reduce (mut, left)
        

        浅合并按预期工作

        const x =
          [ 1, 2, 3, 4, 5 ]
        
        const y =
          [ 0, 0, 0 ]
        
        const z =
          [ , , , , , 6 ]
        
        console.log (merge (x, y))
        // [ 0, 0, 0, 4, 5 ]
        
        console.log (merge (y, z))
        // [ 0, 0, 0, <2 empty items>, 6 ]
        
        console.log (merge (x, z))
        // [ 1, 2, 3, 4, 5, 6 ]
        

        还有深度合并

        const x =
          { a: [ { b: 1 }, { c: 1 } ] }
        
        const y =
          { a: [ { d: 2 }, { c: 2 }, { e: 2 } ] }
        
        console.log (merge (x, y))
        // { a: [ { b: 1, d: 2 }, { c: 2 }, { e: 2 } ] }
        

        可变参数合并

        也许我们想要一个不限于两个输入的合并函数; mergeAll

        const Empty =
          {}
        
        const mergeAll = (first = Empty, ...rest) =>
          first === Empty
            ? first
            : merge (first, mergeAll (...rest))
        
        mergeAll ({ a: 1 }, { b: 2 }, { c: 3 })
        // { a: 1, b: 2, c: 3 }
        

        此答案摘自另一个问题:How to compare two objects and get key-value pairs of their differences?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-04
          • 1970-01-01
          • 1970-01-01
          • 2013-12-09
          相关资源
          最近更新 更多