0

So I have a form handling system set up where I can pass value formatting options that will change the input to fit a certain formatting (for instance stripping non-number values, enforcing max-length, adding hyphens at every X interval etc) and I want to create a function that creates a combined formatter of an arbitrary number of more minute formatters, and runs the value through these in order.

For instance a formatter could be, in the case of numbers only:

const numberFormatter = value => value.replace(.replace(/[^\d]/g, '');

But I also have createFormatter functions, like for max length:

export const createMaxLengthFormatter = (maxLength) => (value) => value
  .slice(0, maxLength);

The way I use them is usually like this:

const someNumberInput = createFormField({
   ...otherParams,
  valueFormatter: createMaxLengthFormatter(8),
})

However I want to be able to do something akin to this:

const someFourDigitNumberInput = createFormField({
  ...otherParams,
  valueFormatter: combineFormatters(numberFormatter, createMaxLengthFormatter(4)),
});

Any suggestions how to implement combineFormatters in an elegant ESNEXT way?

3
  • you want to compose the two functions. googling for es6 compose functions will answer your question. function composition is a general concept. Commented Dec 17, 2020 at 2:29
  • Thanks for pointing me in the right direction @AndyRay ! Looking up on it now Commented Dec 17, 2020 at 2:32
  • Hmm, I ended up with a solution kind of based on the documentation I found on es6 composing, just a bit simplified using .reduce to iterate and use the value of the previous computation as the input for the next. I guess I'll share my own answer but see if something more elegant pops up Commented Dec 17, 2020 at 2:39

2 Answers 2

2

So thanks to @andy-ray pointing me in the right direction I ended up with one solution that seems to be working fine:

export const combineFormatters = (...formatters) => (value) => formatters
  .reduce((currentValue, formatter) => formatter(currentValue), value);

Leaving it up as a solution for now, but not marking it as the correct one in case somebody comes up with a more elegant/correct solution for this problem. Though it does indeed seem to be doing the job as intended.

Sign up to request clarification or add additional context in comments.

1 Comment

That's almost exactly what I was about to post, so I think it's a good solution.
1

This is a typical case of function composition. The compose high order function would goes like:

// compose from right to left
const compose = (...fns) => fns.reduce((f, g) => x => f(g(x)))

// compose from left to right
const compose = (...fns) => fns.reduce((f, g) => x => g(f(x)))

I would say this implementation is elegant in the sense that it looks so close to the mathematical expression. Pleasing to the eye!

However it will produce a deep calling stack, because of the function-inside-function form. Your implementation would be more memory friendly, and is equivalent in effect.

2 Comments

Ah, I see. Yeah it's absolutely beautiful to look at, but I see what you mean, the recursive nature of it would increase the callstack. So I guess in my specific case reusing the result and successively calling funcitons with the previous output is more efficient.
I would just say it's more memory efficient. CPU efficiency is theoretically identical. The difference is the order of calling CPU instructions, not the number of instructions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.