使用 Ramda 递归 "merge" 或 "extend"?

Recursive "merge" or "extend" with Ramda?

我试图找到一个与 Lodash's merge using Ramda 等效的函数,该函数执行基于 "merge" 或 "extend" 的递归对象键。行为类似于以下内容:

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 that R.set was briefly introduced, but then rolled back 此后不久。从那以后 Ramda 库是否捕获了此功能?

Ramda 是否提供此功能?

Ramda 目前不包含这样的函数。

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

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

更新

(两年后。)这最终以几个函数的形式添加:mergeDeepLeft, mergeDeepRight, mergeDeepWith, and mergeDeepWithKey

可以使用 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"}

Ramda 现在有几个合并函数:mergeDeepLeft, mergeDeepRight, mergeDeepWith, mergeDeepWithKey

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>

从零开始

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?