使用Ramda映射和过滤对象

时间:2015-07-29 13:03:49

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

我正在学习Ramda,并且我有点困惑如何使用lodash在下方构建此Ramda链。 Ramda返回其操作的函数而不是实际值,这似乎是函数式编程的焦点,但是在这个例子中,我有第二个参数localRegex,它不是' ta主要论点。似乎不可能在不包装Ramda函数并使用.apply().call()将包装函数参数传播到{{{}}的情况下完全复制它是不可能的。 1}}函数,使用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中复制使用getRecursiveDeps的{​​{1}}?

有很多关于lodash如何运作以及RamdaRamda不是很好的讨论。但在这种情况下,underscore是一个从lodash返回值的函数。当您从getRecursiveDepslodash创建这样的函数时,结果是相同的,在包装它时会有更多的工作,在这种情况下,使用{{ 1}}超过lodash

2 个答案:

答案 0 :(得分:7)

R.compose_.chain完全不同。根据当前的文档,它的类型是(a -> [b]) -> [a] -> [b],尽管它的实际类型更为通用。把它想象成一个“平面图”功能。

您实际需要的是R.pipe或从左到右的等效this

如果函数的目标是找到本地依赖项,那么将模式嵌入函数中似乎是合适的。我会写:

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

getLocalDeps(items);  // => ['./foo']

我对名称getRecursiveDeps感到有点困惑,因为函数不是递归的。 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$/)));

答案 1 :(得分:3)

另一种方法是,无点且保留现有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的一个很好的部分。