Ramda.js refactoring - javascript

I decided to code a simple todo app using Ramda, but I have been stuck with one refactoring related issue. Here're two functions that I think could be refactored:
const isItemCompleted = R.pipe(
R.prop("states"),
R.contains("completed")
)
const isItemEdited = R.pipe(
R.prop("states"),
R.contains("editing")
);
As you can see, there is some code duplication and this would get even messier if I had more states. I have been trying to isolate the duplicated functionality as such:
const statesContains = R.flip(R.pipe(
R.prop('states'),
R.contains()
))
//I would like to use it like this:
const isItemCompleted = statesContains("completed")
const isItemEdited = statesContains("editing")
But I just cannot wrap my head around this. I can make it work with different argument ordering, but I would like to follow the data-last rule to create concise functions.
The data being passed to these isItemCompleted and isItemEdited functions could be something like this:
let item = {states:["editing", "complete"]};
isItemCompleted(item); //true
Any (functional) ideas?

There are several ways to go with this.
Perhaps the most straightforward is
const statesContains = R.curry(
(state, item) => R.contains(state, R.prop('states', item))
);
const isItemCompleted = statesContains("completed");
But it's reasonable to want to abstract this a bit, to allow the property to be searched to vary as well. So you could write:
const propContains = R.curry(
(propName, state, item) => R.contains(state, R.prop(propName, item))
);
const editorsContains = propContains('editors')
const edFred = editorsContains('fred');
// or edFred = propContains('editors', 'fred');
Both of these are reasonable. But Ramda has a function which reads really well, and will serve these needs pretty well, where. With this, you can simply write:
const isItemCompleted = R.where({states: R.contains('completed')});
This, I believe, is the simplest approach if you're looking for one-offs. But both of the above could help you create reusable functions.
You can see all this in action in the Ramda REPL

Related

Ramda point-free problems with transform functions (map, etc)

I have this function, which is basically mapping request parameters and query parameters to a SQL statement:
function selectSingleResult(params, { order, limit, offset, fields, runset_id, txOffset, txFactor }) {
const transformer = R.when(R.equals('values'), R.compose(knex.raw.bind(knex), applyConversion({ txOffset, txFactor })))
const newFields = R.map(transformer , fields);
knex('myTable').select(...newFields) // etc...
Ideally, I would like to be able to define transformer outside the function so the function can just become:
const mapFields = R.map(transformer);
function selectSingleResult(params, { order, limit, offset, fields, runset_id, txOffset, txFactor }) {
knex('myTable').select(...mapFields(fields)) // etc...
The issue being that the applyConversion function needs arguments given to selectSingleResult.
This is a common issue I have with transform functions to map. They often require parameters other than the values being mapped over. In such cases, how can I write in point-free, or at least more testable style and not end up nesting functions so much?
It feels as though you're trying to go point-free in code where it doesn't make much sense. But there's a contrasting notion of destructuring a large set of fields from the second parameter that doesn't seem necessary, so this seems mostly confused. (It might actually be necessary: perhaps you're using order, limit, offset, and runset_id in the remainder of your main function.)
I think you can accomplish almost what you're asking by just introducing one more layer of calls, with something like this:
const transformer = (query) =>
R .when (R .equals ('values'), field => knex .raw (applyConversion (query) (field)))
const mapFields = R.pipe (transformer, map)
const selectSingleResult = (params, query) => {
knex ('myTable') .select (... mapFields (query) (query .fields))
// etc...
}
I name the second parameter to your main function query; it's a guess, and if that's confusing, replace all the instances of query with foo or with something meaningful to you.
Note that mapFields could also be written as const mapFields = (query) => R.map (transformer (query)), but the version above seems simpler. Also note that I simplified transformer a bit. I simply don't see any reason to try to go point-free, when you can't get all the way there. And trying to mix point-free code with OO constructs such as knex .raw just seems to confuse things.
If I read the code correctly, we also might rewrite transformer like this:
const transformer = (query) => (field) =>
field == 'values' ? knex .raw (applyConversion (query) ('values')) : field
but I can't decide if that is an improvement or not.

How to write more shortly?

I want to write more shortly. It might be written in one line. How can I do that?
this.xxx = smt.filter(item => item.Id === this.smtStatus.ONE);
this.yyy = smt.filter(item => item.Id === this.smtStatus.TWO);
this.zzz = smt.filter(item => item.Id === this.smtStatus.THREE);
Which array methods should I use?
Thanks for your help!
Well you could shorten it using some destructuring, assuming this is the current scope:
const [xxx, yyy, zzz] = ['ONE', 'TWO', 'THREE'].map(x => smt.filter(item => item.Id === this.smtStatus[x]));
That said, I wouldn't do this, as it's harder to read and maintain.
Steve's answer also gave me another idea, you can use an getter function in your filter function to make your code more compact:
function filterItemListById(list, getter) {
return list.filter(item => item.Id === getter(this.smtStatus));
}
this.xxx = filterItemListById(smt, s => s.ONE);
this.yyy = filterItemListById(smt, s => s.TWO);
this.zzz = filterItemListById(smt, s => s.THREE);
Just because something is short, doesn't mean it is good/better. Code that "reads" should be the priority both for yourself and your teammates.
If you just don't like the look of this, you could always toss the filtering into its own function. Might look like this:
filterItemListById(list, value) {
if (list == null) return null;
return list.filter(item => item.Id === value);
}
this.xxx = filterItemListById(smt, this.smtStatus.ONE);
// etc...
Now, that is assuming you did NOT mean to combine all three lines into one. If that is what you meant, well...
Ok, so you can't assign to multiple variables (this.xxx, this.yyy, this.zzz) like that, usually. I know some people declare multiple variables in one line like:
var myInt1= 0, myInt2= 1, myInt3 = 2; // and so on
Declaring multiple primitives like this is fine, but I would never do this with any complicated logic, too messy looking.
To shorten, you have two options:
Put your logic into one or more functions with descriptive names, nothing wrong with this.
Put your variables into a list and loop over those variables while filtering. This is an awful approach, I like what you already have just fine, nothing wrong with it.
In short, don't worry about writing cute one-liners, you will only confuse yourself and your team later on. Focus on writing readable code so anyone can understand what is going on just by reading it line by line.

compose chained functions using the same parameter in javascript

I am trying to better understand functional composition in ramda.js and currently have a method in a class that looks like this:
const replace = (newItem) => function3(function1(newItem).function2(newItem));
I know in ramda.js for a simpler function like
const replace = (newItem) => function2(function1(newItem));
you could write it like
const replace = compose(function2, function1);
Is there a similar way to do the same with the initial function using functional composition / application or other ramda.js helper methods?
Ramda has two functions that should help with this. The more standard one is lift. Many functional languages have this concept. One way to think about it is that it lifts a function which operates on values to create one that operates on containers of those values:
add(3, 5) //=> 8
lift(add)([3], [5]) //=> [8]
Functions can be seen as containers of values too. Functions which return values of a given type can be considered containers for that type.
So we can lift your function3 to operate not on values, but on containers for those values, and then supply it the input to those functions. Here's an example with arrays as containers:
const function1 = newItem => `function1(${newItem})`
const function2 = newItem => `function2(${newItem})`
const function3 = (v1, v2) => `function3(${v1}, ${v2})`
const combine = R.lift(function3)(function1, function2)
console.log(combine('foo')) //=> "function3(function1(foo), function2(foo))"
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
The less standard function is converge. This is focused only on functions, and not on arbitrary containers. It works similarly in this case. The function is created in one pass rather than the two for lift. And that means the initial functions need to be wrapped in an array:
const function1 = newItem => `function1(${newItem})`
const function2 = newItem => `function2(${newItem})`
const function3 = (v1, v2) => `function3(${v1}, ${v2})`
const combine = R.converge(function3, [function1, function2])
console.log(combine('bar')) //=> "function3(function1(bar), function2(bar))"
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
converge is only for functions, but it can work with polyadic functions. lift will work only with unary ones. converge is a Ramda-specific function. I haven't seen it elsewhere. So, if lift will work for you, I would suggest you choose it instead.
So your question is how to write
function1(input).function2(input)
In a functional way. If I am correct, here is how:
First let's create a function method that would give us a method of an object bound to that object:
const method = R.curry((name, object) => R.bind(object[name], object))
Using this function, we can rewrite our expression as
method('function2', function1(input))(input)
But we want something cleaner and more re-usable, right? So let's do some refactoring
method('function2', function1(input))(input)
method('function2')(function1(input))(input)
R.pipe(function1, method('function2'))(input)(input) // or use R.compose
R.converge(R.call, [
R.pipe(function1, method('function2')),
R.identity
])(input)
Now we can define our function combine like so
const combine = (fn, name) =>
R.converge(R.call, [
R.pipe(fn, method(name)),
R.identity
])
And the expression becomes
combine(function1, 'function2')(input)
I hope my explanation is clear and it solves your problem :)

lodash fp and refactoring some existing code

I'm learning functional programming and I'm trying to refactor a piece of old code using lodash FP.
Here's the code I have:
_.filter(x => isIdInArray(x)(xs))(otherXs)
It's too complicated to read and makes me feel a bit weird (smell ?)
My problem is that x value, that is the first argument of isIdInArray is declared this way:
const getId = _.get('id');
const isIdInArray = _.compose(_.includes, getId);
I can't use my lodash filter function this way:
_.filter(isIdInArray(xs))(otherXs)
I don't even know if it's feasible, but I'm pretty sure I can do something clearer or more readable.
Do you have some ideas ?
Try not to stuff all the fancy features that lodash gives you into a single line there.
Having a complex mechanism in one line may seem nice, but if you can't read it anymore its not very helpful at all.
For managing collections i usually use approaches like this:
var collection = [{id: 'a', someField: 1}, {id:'b', someField: 2}];
var theIdsYoureLookingFor = ['a'];
var filtered = collection
.filter(anyObject => _.includes(theIdsYoureLookingFor, anyObject.id))
.map(anyObject => anyObject.someField);
alert(filtered); // alerts 1
Which parses a collection of objects, filters for those who have an id that you consider valid and then maps those objects to a certain field.
Also don't ever use variable names like: x, xs
If you're writing production code, I recommend using a higher-level function. In your particular case I'd say you need _.intersectionBy:
const keepIfIdInArray = _.intersectionBy('id'); // 'id' can be replaced by your getId
const keepIfIdInOtherXs = keepIfIdInArray(otherXs);
keepIfIdInOtherXs(xs);
If you're doing this as an exercice, then I'd say you may need to decompose a little more. Notice that in lodash/fp, _.includes is curried so you should be able to write the following:
const getId = _.get('id');
const isIdInArray = arr => _.compose(_.includes(arr), getId);
const isIdInOtherXs = isIdInArray(otherXs);
_.filter(isIdInOtherXs)(xs);

Handling User Configuration In Functional Programming

I have a series of (pure) functions responsible for expanding tokens held in an object graph.
I won't go into the details as they aren't important, but they are something like:
expandData => expandDataNodes => expandDataNode => expandDataNodeItem
I have everything working fine, but I now have a new requirement which means I now need user defined configuration to be available in expandDataNodeItem. Obviously in the OO world this is a trivial problem to solve just make the config available on the DataExpander class via a getter and pull it out when needed, but I'm interested in the options for handling this situation when using a functional paradigm.
I know I can add a param to each of these functions and pass down either the option or a function for accessing the option, but this results in lots of functions which have no interest in the option getting it added to their signatures which feels like it really muddies things.
The best option I can think of at the moment is to wrap all these functions in another function and use closure to make the options available to all functions within, but is there a better way?
With these few details I would try another approach: you may refactor your code so the functionality that requires some configuration may come already injected in a partially-applied function. That is, your current function should be a high-order function.
Check the following sample on which I demonstrate that approach:
const f = ( { config1, configN } ) => x => config1 + x
const g = f => x => f ( x )
const settings = {
config1: 1
}
// _f would be the function that you would provide
// when you need to do certain operation based on
// who knows what configuration
const _f = f ( settings )
const y = g ( _f ) ( 2 )
console.log( y )

Categories