I have to deal with parsing XML from a ftp, and many times I had to wrote a condition in order to ensure a list as output of my function, example:
const getCollaborators = pipe(
propOr({ PERSON: [] }, 'OFFICETEAM'),
propOr([], 'PERSON'),
collaborators => (isNil(collaborators)
? []
: is(Array, collaborators)
? collaborators
: [collaborators]) // single
)
The problem is on the last five lines, repeated a lot of times in my code, and I can't do this if I embrace DRY concept.
So, what about adding a tiny helper to do this job? asList? 鉃★笍
const asList = a => Array.isArray(a)
? a
: a === null || a === undefined
? []
: [a]
// refactored code
const getCollaborators = pipe(
propOr({ PERSON: [] }, 'OFFICETEAM'),
propOr([], 'PERSON'),
asList
)
I'm skeptical about adding the isNil logic in asList, as it can be a constraint in some cases.
You can try using R.cond for multiple conditions.
const asList = R.cond([
[R.isNil, R.always([])],
[R.is(Array), R.identity],
[R.T, R.of] //
]);
const getCollaborators = R.pipe(
R.propOr({PERSON: []}, 'OFFICETEAM'),
R.propOr([], 'PERSON'),
asList
);
Sure it doesn't look pretty but its more modular and takes care of your constraint problem.
@Braagaa Yeah sure. I didn't think about the best implementation but rather the idea, and @CrossEye told me in a previous PR to avoid the usage of internals functions (when creating a new one), in order to avoid perf problems: https://github.com/ramda/ramda/issues/2692#issuecomment-437136238
I'm afraid that I personally don't like this function. It makes too many assumptions about user intent, and it goes against our notion of simplicity. We try to ensure that Ramda functions have one straightforward signature, even if that signature is not easily expressed as a Hindley-Milner type expression.
As described, this function accepts an array, a nil value, or a scalar value, and returns an appropriate array. When Ramda functions accept different types it's because there is some common underlying type. For instance, we supply implementations to allow map to accept objects, arrays, and functions, because all of those are Functors. (We also delegate map to those things that claim to be or look like Functors, but that's a separate concern.) It's hard to see what's the common type between these varied inputs. That's probably enough of a reason not to include this.
But there's another concern: we really don't like making the kind of assumption that this requires. Imagine that you're working with a list of values in some Color class, and you call asList on a candidate Color or list of Colors. It works fine. Then one day, you realize that you are making no use of this as a class, and you could easily replace all your constructed Color objects with simple rgb(a) arrays. Now when you expect a list of colors and call asList, it works as expected with arrays of color arrays or with nil values, but it fails when you pass it a single color array. This function assumed that your array of numbers would do fine, when what you really wanted was an array of arrays of numbers. Ramda tries hard to avoid ever being the cause of this sort of failure.
Thank you for the suggestion, and I'll leave it open to hear if others, especially those in @ramda/core, have any differing thoughts, but my inclination is a strong no.
Most helpful comment
You can try using R.cond for multiple conditions.
Sure it doesn't look pretty but its more modular and takes care of your constraint problem.