【问题标题】:Map and filter an object using Ramda使用 Ramda 映射和过滤对象
【发布时间】:2015-10-20 11:32:42
【问题描述】:

我正在学习Ramda,我有点困惑如何使用Ramda 在下面构建这个lodash 链。 Ramda 返回其操作的函数而不是实际值,这似乎是函数式编程的重点,但是在此示例中,我有第二个参数 localRegex 不是主要参数。如果不包装 Ramda 函数并使用 .apply() 或 .call() 将包装的函数参数传播到 Ramda 函数,似乎不可能完全复制它,这似乎更复杂使用lodash。

var _ = require("lodash")
var R = require("ramda")

var localRegex = /^.\.\/|^.\/|^\//

function getRecursiveDeps(deps, localRegex){
  return _.chain(deps)
    .map(function(dep){
      return dep.source.value
    })
    .filter(function(dep){
      return dep.match(localRegex)
    })
    .value()
}

var items = [
  {
    "source": {
      "value": "./foo"
    }
  },
  {
    "source": {
      "value": "bar"
    }
  }
]

console.log(getRecursiveDeps(items, localRegex))

这是我所拥有的,但它不起作用。

var getRecursiveDeps = R.chain(
  R.map(function(dependency){
    return dependency.source.value
  }),
  R.filter(function(value){
    return value.match(localRegex)
  })
)

有没有办法让Ramda 使用主变量进行链接并向下传递localRegex?有没有办法在Ramda 中复制使用lodash 的getRecursiveDeps?

关于Ramda 的功能性以及underscore 和lodash 的功能性有很多讨论。但在这种情况下,getRecursiveDeps 是一个从lodash 返回值的函数。当你从lodash 或underscore 创建这样的函数时,结果是一样的,在包装它时还有更多的工作,在这种情况下,使用Ramda 而不是Lodash 会有什么好处?

【问题讨论】:

    标签: javascript dictionary functional-programming lodash ramda.js


    【解决方案1】:

    R.chain 所做的事情与_.chain 完全不同。根据当前文档,它的类型是(a -> [b]) -> [a] -> [b],尽管它的实际类型更通用。将其视为“平面地图”功能。

    您真正想要的是R.compose 或其从左到右的等价物R.pipe。

    如果函数的目标是查找本地依赖项,我认为将模式嵌入函数中似乎是合适的。我会这样写:

    // getLocalDeps :: [{ source :: { value :: String }}] -> [String]
    const getLocalDeps =
    R.pipe(R.map(R.path(['source', 'value'])),
           R.filter(R.test(/^[.]{0,2}[/]/)));
    
    getLocalDeps(items);  // => ['./foo']
    

    我对@9​​87654330@ 这个名字有点困惑,因为这个函数不是递归的。 getLocalDeps 似乎更合适。


    如果您想对模式进行参数化,我建议将getLocalDeps 分解成更小的部分:

    // isLocal :: String -> Boolean
    const isLocal = R.test(/^[.]{0,2}[/]/);
    
    // getDeps :: [{ source :: { value :: String }}] -> [String]
    const getDeps = R.map(R.path(['source', 'value']));
    
    // getLocalDeps :: [{ source :: { value :: String }}] -> [String]
    const getLocalDeps = R.pipe(getDeps, R.filter(isLocal));
    

    然后您可以根据这些构建块定义其他功能:

    // getNonLocalDeps :: [{ source :: { value :: String }}] -> [String]
    const getNonLocalDeps = R.pipe(getDeps, R.reject(isLocal));
    
    // getLocalJsonDeps :: [{ source :: { value :: String }}] -> [String]
    const getLocalJsonDeps = R.pipe(getLocalDeps, R.filter(R.test(/[.]json$/)));
    

    【讨论】:

    • 您好,谢谢您的回答!这个函数是一个中介,它为我的代码中的更精细的recursiveDeps 函数提供更多的本地依赖项,并去除不需要迭代的模块。
    • 有什么方法可以使用 Ramda 提供辅助参数吗?还是当参数更通用时,我是否不得不将其包装在一个函数中?
    • 我在答案中添加了一种参数化第二个参数的方法。
    【解决方案2】:

    另一种做到这一点的方法,免积分并保留您现有的 API,如下所示:

    // getLocalDeps :: [{ source :: { value :: String }}] -> RegExp -> [String]
    const getLocalDeps =  R.useWith(
      R.flip(R.call),
      R.map(R.path(['source', 'value'])),
      R.pipe(R.unary(R.test), R.filter)
    );
    
    localDeps(items, localRegex); //=> ["./foo"]
    

    函数的最后一行让我觉得有点不幸,这个问题让我open an issue 关于恢复库最近的一些更改。可以使用多种变体:

    // ...
    R.pipe(regex => item => R.test(regex, item), R.filter)
    //...
    

    或

    // ...
    regex => R.filter(R.test(regex))
    //...
    

    但在最近对 Ramda 进行更改之前,它本来可以很简单

    // ...
    R.pipe(R.test, R.filter)
    //...
    

    不过,有一点是 Ramda 努力使参数保持逻辑顺序:那些不太可能改变的参数在那些更可能改变的参数之前出现。考虑到这一点,我更喜欢这样的东西:

    // getLocalDeps :: RegExp -> [{ source :: { value :: String }}] -> [String]
    var getLocalDeps2 =  R.useWith(
      R.call,
      R.pipe(R.unary(R.test), R.filter),
      R.map(R.path(['source', 'value']))
    );
    
    localDeps2(localRegex, items); //=> ["./foo"]
    

    而且感觉更干净了。此外,它还允许您预定义函数并单独使用它:

    myDeps = localDeps2(localRegex);
    myDeps(items); //=> ["./foo"]
    

    这是 Ramda 的一个很好的部分。

    【讨论】:

      猜你喜欢
      • 2018-11-18
      • 2020-06-27
      • 2022-08-16
      • 2019-09-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-28
      • 2021-05-08
      相关资源
      最近更新 更多