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);
Related
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.
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 :)
Most elegant solution required please. We all deal with objects that can be constructed incrementally throughout their lifecycle. Passing object references is the most useful way of dealing with objects, as mutations affect the original rather than just a copy.
For example:
const bigObject = fetchBigObjectFromStorage()
let myMem = bigObject.member || {}
If member exists within bigObject, myMem gets a reference to it. If not, it just receives a reference to a new empty object. So this has unpredictable results:
myMem.value = 30
No errors. myMem.value is now 30. But did the value end up where we actually wanted it?
console.log(bigObject.member.value)
This either displays 30 or gives a TypeError because bigObject.member is undefined, depending on whether or not bigObject.member existed already.
To avoid errors, we could put the new value back into bigObject, and get it put back there every time, we'd need to explicitly assign it thus:
bigObject.member = myMem
which is messy.
So we could pre-empt the possibility that bigObject.member isn't there, test for it, and insert it before we start trying to pass a reference to it around, even initialising required members with null values. So we have this code:
const bigObject = fetchBigObjectFromStorage()
if (!bigObject.hasOwnProperty("member")) bigObject.member = {value: null}
let myMem = bigObject.member
myMem.value = 30
console.log(bigObject.member.value)
... and we'll get no errors, 30 displayed, and reliable behaviour every time (well, almost... a couple more tests in the if might make it watertight). But it's as ugly as sin and too much code for something so trivial. We could refactor bits of it like that if statement into a little function, make it a generic member initialiser, and a few other approaches besides. I'm sure most of us have tried lots of techniques over the years. But what's the most elegant, most favoured solution from the assembled learned throng? Any clever little tricks?
You can shorten that if statement using a conditional operator:
const myMem = bigObject.hasOwnProperty("member")
? bigObject.member
: (bigObject.member = {value: null});
And if the truthiness check is enough for you, then you can further simplify with a short-circuit logic operator:
const myMem = bigObject.member || (bigObject.member = {value: null});
The probably most elegant way is offered by ES6 destructuring with a default initaliser, which implicitly tests for undefined:
const { member: myMem = (bigObject.member = {value: null}) } = bigObject;
How about
const bigObject = {...defaults, ...fetchBigObjectFromStorage()}
where defaults is something like:
{member: {value: null} etc}
This way, your object always has all expected fields in place.
let myMem = bigObject.member = bigObject.member || {};
myMem.value = 30;
console.log(bigObject.member.value); // outputs 30
I think this could be one of the trick
I've been using and loving babel (6.5.2) for a while now and find the new destructuring syntax great for writing clearer JavaScript.
Why doesn't the rest destructuring work (it generates a token error) anywhere in the array?
For example:
const [column, ...restOfColumns] = columns;
const objProps = column.valueChain.slice(0, -1);
const prop = column.valueChain[column.valueChain.length - 1];
//const [...objProps, prop] = column.valueChain
The commented out line would replace the preceding two lines with something much easier to read and understand.
The simple answer is that when you use the destructing syntax ..., it means everything else. Therefore when you try [...objProps, prop], it doesn't know what to assign to prop as you have assigned all values already to objProps
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