# Refactoring vanilla JS code to be more DRY

In developer-speak, *DRY* is an acronym that stands for *Don’t Repeat Yourself*.

Today, I’m going to look at a bit of code that includes some repetition and redundancy, and refactor it to be more *DRY*.

## The starting code

Yesterday, we created three vanilla JS helper functions for rounding numbers to the nearest integer.

One rounded up. One rounded down. And the last rounded up if the number to be rounded was 5 or greater, and down if it was less than 5.

```
/**
* Round to the nearest whole number
* @param {Number|String} num The number to round
* @param {Number} precision The whole number to round to (ex. 10, 100, 1000)
* @return {String} The rounded, delimited number
*/
var round = function (num, precision) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
return (Math.round(num / precision) * precision).toLocaleString();
};
/**
* Round down to the nearest whole number
* @param {Number|String} num The number to round
* @param {Number} precision The whole number to round to (ex. 10, 100, 1000)
* @return {String} The rounded, delimited number
*/
var roundDown = function (num, precision) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
return (Math.floor(num / precision) * precision).toLocaleString();
};
/**
* Round up to the nearest whole number
* @param {Number|String} num The number to round
* @param {Number} precision The whole number to round to (ex. 10, 100, 1000)
* @return {String} The rounded, delimited number
*/
var roundUp = function (num, precision) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
return (Math.ceil(num / precision) * precision).toLocaleString();
};
```

As you can see, there’s quite a bit of shared code between them. Let’s fix that.

## An easy approach: pass in the function as an argument

A simple way to handle this would be to make developers pass in a rounding function as an argument.

First, let’s add an argument, `fn`

, to the function.

```
var round = function (num, precision, fn) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
return (Math.round(num / precision) * precision).toLocaleString();
};
```

Next, let’s set a default a value isn’t provided.

```
var round = function (num, precision, fn) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
if (!fn || typeof fn !== 'function') {
fn = Math.round;
}
return (Math.round(num / precision) * precision).toLocaleString();
};
```

Finally, we’ll use `fn()`

in place of our `Math`

method.

```
var round = function (num, precision, fn) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
if (!fn || typeof fn !== 'function') {
fn = Math.round;
}
return (fn(num / precision) * precision).toLocaleString();
};
```

## A more friendly approach: named rounding methods

The previous approach is simple, but requires developers to pass in a rounding function. A more elegant approach would let them pass in their desired rounding type—`up`

, `down`

, or `auto`

—and figure out the rest for them.

First, let’s add a `method`

argument to our function.

```
var round = function (num, precision, method) {
num = parseFloat(num);
if (!precision) return num.toLocaleString();
return (Math.round(num / precision) * precision).toLocaleString();
};
```

Next, we’ll create an object of valid methods, and the `Math`

functions that they map to. Instead of forcing users to pass in `ceil`

or `floor`

as arguments, let’s go with the more obvious `up`

, `down`

, and `auto`

(the default if they pass in nothing).

I’ve also added some comments to the code, since it’s grown beyond our original three-liner.

```
var round = function (num, precision, method) {
// Convert string numbers to a float
num = parseFloat(num);
// If there's no rounding precision, return the number
if (!precision) return num.toLocaleString();
// Possible methods and their values
var methods = {
auto: 'round',
up: 'ceil',
down: 'floor'
};
// Do math!
return (Math.round(num / precision) * precision).toLocaleString();
};
```

Now, we can get the `Math`

function for the user’s desired rounding style by passing it into our `methods`

object. To be safe, if no matching method exists, we’ll fallback to `round`

.

The `fn`

variable will now have a value of `round`

, `ceil`

, or `floor`

.

```
var round = function (num, precision, method) {
// Convert string numbers to a float
num = parseFloat(num);
// If there's no rounding precision, return the number
if (!precision) return num.toLocaleString();
// Possible methods and their values
var methods = {
auto: 'round',
up: 'ceil',
down: 'floor'
};
// Get the method function
var fn = methods[method];
if (!fn) {
fn = 'round';
}
// Do math!
return (Math.round(num / precision) * precision).toLocaleString();
};
```

Finally, because the `.round()`

, `.ceil()`

, and `.floor()`

methods are all properties of the `Math`

object, we can call our `fn`

variable on `Math`

with bracket notation to get the function.

For example, if `fn`

had a value of `floor`

, `Math[fn]()`

would be the same as writing `Math.floor()`

.

```
var round = function (num, precision, method) {
// Convert string numbers to a float
num = parseFloat(num);
// If there's no rounding precision, return the number
if (!precision) return num.toLocaleString();
// Possible methods and their values
var methods = {
auto: 'round',
up: 'ceil',
down: 'floor'
};
// Get the method function
var fn = methods[method];
if (!fn) {
fn = 'round';
}
// Do math!
return (Math[fn](num / precision) * precision).toLocaleString();
};
```

Here’s another demo with this approach.

## Which one should you use?

It depends on the audience.

For a public “plugin” designed to be used by total beginners, I would use the second “more elegant” approach. It’s more “plain English”, and prevents the user from messing things up if they pass in a bad/incorrect function.

For my own personal use, and for use by developers looking to keep code as lean as possible, I would use the first because it’s tiny and possibly more flexible. For example, if a developer wanted to write their own custom rounding function to use instead of one of the `Math`

methods, they could pass that in, too.

I’ve updated the Vanilla JS Toolkit with the second approach.

It still minifies down to less than half the size of the three other methods combined. It’s only a little bigger than any one of them, and almost the same size as the first method (after minification).