I am using Ramda.js for selector functions, to access data in a Redux store. What I want is to define my selectors as functions not referencing the state
that the selectors act on, like:
const getUserName = path(['user', 'name']);
const name = getUserName({
user: {
name: 'Some Name'
}
});
This is easy for simple selectors, but sometimes becomes a problem for composed selectors.
Here is an example, where some items
needs to be resolved, referenced by their id
on an object:
const getItemById = id => state => path(['items', id], state);
const getConnectedItemIds = obj => path(['items'], obj);
const getItemsFromObj = obj => state => {
const ids = getConnectedItemIds(obj);
return ids.map(id => getItemById(id)(state));
};
The first function can easily be expressed without reference to state
, and the second function without obj
, something I believe is called point-free style. But how to write the third function without state
?
I am looking for how to rewrite the third function using Ramda, but also rules and procedures regarding this, such as (without knowing if its true):
All composed functions need to have state
as their last argument to be able to pull it out in the final composition.
There are many good suggestions already here. Probably most important is the advice to use point-free only when it improves readability, and not as a goal on its own.
My answer does use a point-free version of your main function and of one of your helpers, but skips it for the other, where I think readability would suffer.
const getItemById = id => path(['items', id]);
const getConnectedItemIds = prop ('items');
const getItemsFromObj = pipe (
getConnectedItemIds,
map (getItemById),
juxt
)
const obj = {foo: 42, items: ['8', '17']}
const state = {bar: 1, items: {'6': 'a', '8': 'b', '14': 'c', '17': 'd', '25': 'e'}}
console .log (
getItemsFromObj (obj) (state)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {juxt, map, path, pipe, prop} = R </script>
The important function here is juxt
, which applies an array of functions to the same values, returning an array of the results.
Here getItemById
is simplified from the original, by removing the state
point, but making this point-free can be done only at a cost in readability, as far as I can tell. Other had suggestions for this, all of which are fine, but as they pointed out, none were as readable as the above. My version looked like this:
const getItemById = compose (path, flip (append) (['items']));
// or pipe (flip (append) (['items']), path);
I don't think it's any better or worse than the suggestions in the other answers, but none of them is as easy to read as
const getItemById = id => path(['items', id]);
Finally, if those helper functions are not used elsewhere, I think it can actually improve readability to inline them:
const getItemsFromObj = pipe (
prop ('items'),
map (id => path(['items', id])),
juxt
)
Note, although I didn't use it here, I really do like customcommander's suggestion of propOr([], 'items')
for getConnectedItemIds
. It cleanly removes one potential point of failure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With