最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Map and filter an object using Ramda - Stack Overflow

programmeradmin3浏览0评论

I'm learning Ramda and I'm a little confused how to build this lodash chain below using Ramda. Ramda returns functions for it's operations instead of actual values, and this seems to be the focal point of functional programming, however in this example I have a second parameter localRegex that isn't a primary argument. It seems that it wouldn't be possible to get this to be completely duplicated without wrapping the Ramda function and using .apply() or .call() to propagate the wrapped function arguments to the Ramda function, which seems more complicated then using 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))

Here's what I've got and its not working.

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

Is there a way to have Ramda use a main variable for chaining and also pass down localRegex? Is there a way to duplicate the getRecursiveDeps that uses lodash in Ramda?

There's a lot of talk about how Ramda is functional and underscore and lodash aren't. But in this case the getRecursiveDeps is a function that returns a value from lodash. When you create functions like this from lodash or underscore the result is the same, there's just more work when it comes to wrapping it, in this case what would be the perk using Ramda over Lodash?

I'm learning Ramda and I'm a little confused how to build this lodash chain below using Ramda. Ramda returns functions for it's operations instead of actual values, and this seems to be the focal point of functional programming, however in this example I have a second parameter localRegex that isn't a primary argument. It seems that it wouldn't be possible to get this to be completely duplicated without wrapping the Ramda function and using .apply() or .call() to propagate the wrapped function arguments to the Ramda function, which seems more complicated then using 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))

Here's what I've got and its not working.

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

Is there a way to have Ramda use a main variable for chaining and also pass down localRegex? Is there a way to duplicate the getRecursiveDeps that uses lodash in Ramda?

There's a lot of talk about how Ramda is functional and underscore and lodash aren't. But in this case the getRecursiveDeps is a function that returns a value from lodash. When you create functions like this from lodash or underscore the result is the same, there's just more work when it comes to wrapping it, in this case what would be the perk using Ramda over Lodash?

Share Improve this question edited Jul 29, 2015 at 13:09 ThomasReggi asked Jul 29, 2015 at 13:03 ThomasReggiThomasReggi 59.4k97 gold badges257 silver badges459 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 11

R.chain does something completely different from _.chain. Its type, according to the current documentation, is (a -> [b]) -> [a] -> [b], though its actual type is more general. Think of it as a "flat-map" function.

What you actually want here is R.compose or its left-to-right equivalent R.pipe.

If the goal of the function is to find local dependencies, it seems appropriate to me to embed the pattern in the function. I would thus write:

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

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

I'm a bit confused by the name getRecursiveDeps as the function isn't recursive. getLocalDeps seems more appropriate.


If you'd like to parameterize the pattern, I suggest breaking getLocalDeps into smaller pieces:

// 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));

You could then define other functions in terms of these building blocks:

// 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$/)));

Another way to do this, points-free and preserving your existing API is like this:

// 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"]

The last line of the function feels a bit unfortunate to me, and this question has caused me to open an issue about reverting some recent changes to the library. There are several variants that could be used:

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

or

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

But before a recent change to Ramda, it would have been simply

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

One thing, though, is that Ramda tries hard to keep parameters in a logical order: with those less likely to change coming before those more likely to change. With that in mind, I would prefer something like this:

// 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"]

And that feels cleaner still. Moreover, it lets you predefine the function and use it separately:

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

And that is a good part of what Ramda is about.

发布评论

评论列表(0)

  1. 暂无评论