Map and filter an object using Ramda

I am learning Ramda and I am a little confused how to build this lodash chain below using Ramda . Ramda returns functions for these operations instead of the actual values, and this seems to be the focus of functional programming, however in this example I have a second parameter localRegex , which is not the main argument. It seems that it would be impossible to completely duplicate it without wrapping the Ramda function and using .apply() or .call() to propagate the arguments of the wrapped function to the Ramda function, which seems more complicated than 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)) 

This is what I have and does not work.

 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 the main variable for the chain as well as skip localRegex ? Is there a way to duplicate getRecursiveDeps that uses lodash in Ramda ?

There is a lot of talk about how Ramda is functional, but underscore and lodash are not. But in this case, getRecursiveDeps is a function that returns a value from lodash . When you create such functions from lodash or underscore , the result will be the same, there will simply be more work when it comes to wrapping it, in this case, what will be perk using Ramda over lodash ?

+7
javascript dictionary functional-programming lodash
source share
2 answers

R.chain does something completely different from _.chain . According to current documentation, it is of type (a -> [b]) -> [a] -> [b] , although its actual type is more general. Think of it as a β€œflat map."

Here you really want R.compose or its equivalent R.pipe from left to right.

If the purpose of this function is to search for local dependencies, it seems to me appropriate to embed a template in the function. I would 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 am a little confused by the name getRecursiveDeps as the function is not recursive. getLocalDeps seems more appropriate.


If you want to parameterize a template, I suggest breaking getLocalDeps into smaller parts:

 // 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 can 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$/))); 
+7
source share

Another way to do this, without dots and preserving the existing API, is as follows:

 // 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 seems a little unsuccessful to me, and this question made me open the question of returning some recent changes to the library. There are several options:

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

or

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

But until the recent change in Ramda, that would be simple.

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

However, one thing is that Ramda is trying to keep the parameters in a logical order: those who are less likely to change before they are 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 it's even cleaner. In addition, it allows you to predefine a function and use it separately:

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

And this is a good part of what Ramda is talking about.

+3
source share

All Articles