如何使用无点递归实现使用 Ramda 删除对象中的空值?

Posted

技术标签:

【中文标题】如何使用无点递归实现使用 Ramda 删除对象中的空值?【英文标题】:How do I implement using point-free recursion to remove null values in objects using Ramda? 【发布时间】:2017-04-20 11:35:32 【问题描述】:

我正在学习无点函数,并正在尝试以这种风格实现这种递归空删除器。

有效,但不是无点:

function removeNulls(obj) 
  return R.ifElse(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
      R.filter(R.pipe(R.isNil, R.not)),
      R.map(removeNulls)
    ),
    R.identity
  )(obj)


module.exports = removeNulls

以下是我的无效尝试:

const removeNulls = R.ifElse(
  R.either(R.is(Array), R.is(Object)),
  R.pipe(
    R.filter(R.pipe(R.isNil, R.not)),
    // throws `ReferenceError: removeNulls is not defined`
    R.map(removeNulls)
  ),
  R.identity
)

【问题讨论】:

你不能,因为 javascript 不提供懒惰声明参数的方法。使用 Y 组合器可能会很幸运,但这会变得非常难看。 @Bergi 感谢您的提示,我将保持原样。如果只是为了将来使用,我很想了解更多关于使用 Y 组合器的信息。看来ramda lacks a Y combinator,但我不知道从那里去哪里。谷歌搜索有点困难...... @Bergi 是正确的,你不能用 const... ) const recurseAction = action => ifElse( any(is(Array), is(Object)), pipe( action, map(action) ), identity ) @NigelBenns 你的recurseAction 不是无点的,也不是递归的——它应该是map(recurseAction(action)) 在 JS 中实现一个 Y-combinator 版本很容易。但它不是为了任何实用的东西。 【参考方案1】:

幸运的是,JavaScript 有资源来解决它缺乏惰性的问题。因此,完全可以通过以下方式使用 lambda 函数来声明递归无点解决方案:a => f(a)。只需将R.map(removeNull) 替换为R.map(a => removeNull(a))

const removeNulls = R.ifElse(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
        R.filter(R.pipe(R.isNil, R.not)),
        R.map(a => removeNulls(a))
    ),
    R.identity
)

在您的情况下,我建议您使用R.reject,它与R.filter 相对。由于您否定谓词,R.filter(R.pipe(R.isNil, R.not)) 等于 R.reject(R.isNil)

const removeNulls = R.ifElse(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    ),
    R.identity
)

最后,这个函数有如下结构ifElse(predicate, whenTrue, identity)等于when(predicate, whenTrue)

const removeNulls = R.when(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    )
)

简化版,关于 Declan Whelan 的评论,因为数组是对象

const removeNulls = R.when(
    R.is(Object),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    )
)

【讨论】:

因为数组是一个对象,所以可以做一个小的简化: const removeNulls = R.when( R.is(Object), R.pipe( R.reject(R.isNil), R.map (a => removeNulls(a)) ) ) 但这不是无意义的,OP 的主要要求并不是指的。虽然有很好的优化。 :) @jlouzado 是的 @yosbel 地图中的a 是一个点。如果您只使用R.map(removeNulls),它将无法正常工作。 @jlouzado 所以这是语言的限制,答案在语言允许的范围内是无意义的。你的建议是什么?【参考方案2】:

Javascript 缺乏惰性是这里的双重杀手: 你不能在它的 const 时调用它,因为你在同一个范围内并且它试图在它的定义中解析 resolveNulls。

此外,您不能只使用 map(recurseAction(action)) ,因为定义本身会破坏堆栈,因此您需要将其包装在另一个作用域中:

const ifElse, always, tap, apply, either, is, isNil, not, pipe, filter, map, identity = require('ramda')

const filterNotNull = filter(pipe(isNil, not))
const log = tap(console.log)

const recurseAction =
  action =>
    ifElse(
      either(is(Array), is(Object)),
      pipe(
        action,
        map(a => recurseAction(action)(a))
      ),
      identity
    )

const removeNulls = recurseAction(filterNotNull)

const a = 
  a: null,
  b: "blah",
  c: 2,
  d: undefined,
  e: 
    meow: null,
    blah: undefined,
    jim: 'bob'
  


const b = removeNulls(a)
console.log(b)

【讨论】:

以上是关于如何使用无点递归实现使用 Ramda 删除对象中的空值?的主要内容,如果未能解决你的问题,请参考以下文章

带有 FP 类型的 Ramda

如何将flattenObj函数从ramda cookbook转换为迭代函数

如何使用Ramda将对象数组转换为列表

使用 ramda 将数组数组转换为数组对象

使用 Ramda 在特定条件下添加对象属性值

markdown 使用ramda按嵌套值过滤对象数组