Simplify your JavaScript code with normalizer functions

Tags:

Everyone loves a good if-else tower! What could be simpler than nesting a few conditionals?

if(stuff) {
  if(more stuff) {
    haha();
  }
  else {
    cool();
  }
}

Ok – that isn’t the most amazing thing in the world.

In a simple example like this, it isn’t too bad. But we all know real code is never easy to understand, especially after ten different people have changed it over the course of years!

But what can you do? Sometimes you have complicated logic, and implementing it without a leaning tower of if-elses can be nigh impossible.

Let me show you something called normalizer functions, and how they can help you get rid of some of that complexity.

Example scenario

Before we look at what a normalizer function is, here’s a quick example scenario.

I’ve been working in a codebase with values which have different units. Think of CSS: you can have values like 1px or 50%.

An easy way to represent such a value in JavaScript would be an object like this:

var valueWithUnit = {
  value: 50,
  unit: '%'
};

Yeah, we could store them as strings too, but that would make operating on the values a pain. Imagine you have to sum two values together – every time you need to do that, you must convert between strings and numbers, without losing the unit, which quickly makes your code even more complicated.

A structure like above makes operations convenient, without requiring you to constantly convert between strings and numbers.

However, some operations require the value to be converted to specific units:

//let's say we need to do a task which requires `foo` to be in pixels:
if(foo.unit === '%') {
  foo = convertToPx(foo, parentSize);
}
 
totalPixelWidth += foo.value

It’s not so bad when we lay it out in a simple example like this, but imagine you have several different variables being used. You end up having several ifs, and even more when you need to go between values in more places than one.

Now imagine you have those ifs involved in complicated business logic and a bug snuck in. All those conditionals are going to make it pretty annoying to track what’s happening.

But fear not, we can easily avoid that…

What is a normalizer?

A normalizer is a function which takes a value and ensures it’s of a certain type. But if the value already is of the right type, it returns it untouched.

How is that useful?

In the above example, we saw a conversion function used to go from a non-px value into a px-value.

A conversion function might look like this:

function convertToPx(percentage, parentValue) {
  return {
    value: (percentage.value / 100) * parentValue,
    unit: 'px'
  };
}

We take a percentage value and a parent value, which is used to convert a value into pixels. For example, if a parent element was 100 pixels wide, a value of 50% would get converted into 50px.

But of course this has the problem that in order for it to work, we need to gate it with an if:

if(foo.unit === '%') {
  foo = convertToPx(foo, parentSize);
}
 
totalPixelWidth += foo.value

Every time we have an if-statement in our code, it increases the complexity of that function. If you want to understand what the function is doing, you need to make a mental note about the cases where the code execution would go into the conditional block.

If we instead use a normalizer, we could simplify the code so we don’t need to parse an if statement:

totalPixelWidth += normalizeToPx(foo, parentSize).value;

What would this function look like?

Not very different from the conversion function:

function normalizeToPx(from, parentValue) {
  if(from.unit === 'px') {
    return from;
  }
 
  return {
    value: (from.value / 100) * parentValue,
    unit: 'px'
  };
}

Instead of having the conditional in our main code, we’ve moved it into this function.

This still leaves a function call in place of the if-statement, but it’s a lot easier to follow the logic – there is only one path the code can take, which requires less effort to understand.

In addition, this affords us more flexibility in the future: What if we want to change the behavior of the normalization? If the logic is sprinkled in if-statements, we’d need to change it in multiple places. With a normalizer, we only need to change it in a single location.

Conclusion

The ideal situation with code is that it’s easy for humans to understand.

If we need to read through code to understand what some function is doing, any conditionals will add additional overhead. The more conditionals we have, the more paths the code can take through a function. Imagine if you have to debug code – you can probably see how understanding different code-paths in conditionals would make it harder.

With smart use of functions, we can hide the complexity and the code becomes easier to follow.

While the example here was used for converting between types of values, normalizer functions can be used in many more cases where you have logic that’s gated with an if-statement.