mahadazad
mahadazad

Reputation: 551

How can I improve the ramda code I have written?

I am new to ramda.js, I have written the following code which checks if the value doesn't already end with the suffix in the provided list, it adds "px" suffix


function addSuffix(value, suffix = 'px', test = ['px', 'pt', '%']) {
  return compose(
    concat(value),
    ifElse(anyPass(map(endsWith, test)), () => '', () => suffix)
  )(value);
}

Upvotes: 0

Views: 95

Answers (2)

Hitmands
Hitmands

Reputation: 14199

you could also avoid passing the list of suffixes, since it looks to be redundant... just make sure that the given value doesn't end with a digit:

const ensureSuffix = R.curry((suffix, value) =>
  R.unless(R.test(/\D$/), v => `${v}${suffix}`, value),
);

const addPx = ensureSuffix('px');

console.log(addPx(15));
console.log(addPx('15pt'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

Upvotes: 0

customcommander
customcommander

Reputation: 18961

Perhaps the first suggestion I'd make is to avoid working with default parameters. They usually don't play well with curried functions.

You can write a function that returns a function to checks whether a string ends up with a given list of suffixes:

const hasSuffixFn = compose(anyPass, map(endsWith));
const hasSuffix = hasSuffixFn(['px', 'pt', '%']);

hasSuffix('foo'); // false
hasSuffix('1px'); // true

Then you can have a function that takes a list of suffixes and a suffix and returns a function that will append that suffix if it isn't present already:

const addSuffix = (suffixes, suffix) => unless(hasSuffix(suffixes), flip(concat)(suffix));

const addPx = addSuffix(['px', 'pt', '%'], 'px');
addPx('10'); // '10px'
addPx('10px'); // '10px'
addPx('10pt'); // '10pt'

Please note that you could rewrite addSuffix in a pointfree style with useWith:

const addSuffix = useWith(unless, [hasSuffix, flip(concat)]);

Putting it altogether

const hasSuffix = compose(anyPass, map(endsWith));
const addSuffix = (suffixes, suffix) => unless(hasSuffix(suffixes), flip(concat)(suffix));
const addPx = addSuffix(['px', 'pt', '%'], 'px');

console.log(addPx('10'));
console.log(addPx('10px'));
console.log(addPx('10pt'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {anyPass, endsWith, unless, flip, concat, compose, map} = R;</script>


APPENDIX

Why not using default parameters?

I don't know if using default parameters is a recommend practice in functional programming but I have realised that they usually get in the way when you need to partially apply a function.

Take a look at this:

Here we have a function that adds three numbers:

const foo = (a, b, c) => a + b + c;
foo(10, 20, 30); // 60

We can curry this and start partially applying that function:

const foo_curried = curry(foo);
foo_curried(10, 20, 30); // 60
foo_curried(10, 20)(30); // 60
foo_curried(10)(20, 30); // 60
foo_curried(10)(20)(30); // 60

Let's write a similar function but with default parameters. This works as expected:

const bar = (a=10, b=20, c=30) => a + b + c;
bar();              // 60
bar(110);           // 160
bar(110, 220);      // 360
bar(110, 220, 330); // 660

However if you want to curry this and partially apply it, then you'll get errors:

const bar_curried = curry(bar);
bar_curried();              // 60
bar_curried(110);           // 160
bar_curried(110)(220, 330); // Error!

Why? A curried function waits until you've provided all its arguments. Until then it keeps returning a function that accept the remaining arguments. However if you partially apply a function that has default parameters then you can't really predict what the curried function will return: the final result or a function that accepts the remaining argument?

In the last example, bar_curried(110) returns the result directly, i.e. 110 + 20 + 30 and the error arises when you try to invoke a number as if it was a function.

Upvotes: 2

Related Questions