Photo by Melanie Dijkstra on Unsplash

# Modernizing JavaScript Code: The Comparator Function

After three posts on the subject, I decided to officially make this a new series called Modernizing JavaScript Code.. This one will be short and sweet but still helpful as we continue down the path of learning about functional programming.

## Comparator Functions vs Predicate Functions

Before we go into the code, we need to go into some terminology. There are generally two ways to write functions that compare things. There is the intuitive way which is to take your inputs and return `true`

or `false`

, like this:

```
function checkIsLessThan(x, y) {
return x < y;
}
```

These functions are called **predicates**. Another comparing function will take two values and return a number greater than `0`

if the first value is greater than the second, less than `0`

if it is less than the second, and return `0`

if they are equal. These functions are called **comparators**. It is common to see it used in the `Array.sort`

method:

```
[5, 1, 3, 8].sort((a, b) => a - b);
// [1, 3, 5, 8]
```

The difficulty is that comparators are not always intuitive to write, especially when what you are comparing are not numbers but instead are objects of users. They are also not generally useful outside of those contexts that need them.

In the book, Functional Javascript, we are given a function called `comparator`

that takes a preditcate function and returns an comparator function that does the same logic. Here is that original code example:

```
function comparator(pred) {
return function (x, y) {
if (pred(x, y)) return -1;
else if (pred(y, x)) return 1;
else return 0;
};
}
[5, 1, 2, 8].sort(comparator((a, b) => a < b));
// [1, 2, 5, 8]
[5, "1", "2", 8].sort(comparator(isString));
// ["2", "1", 5, 8]
```

(Technically, in the book, each of the `pred`

calls are wrapped in a function called `truthy`

, but that function is not really part of this discussion, so I am intentionally leaving that out. I think you can guess what the `truthy`

function does on a high level.)

The above code takes a predicate function, and it will return a function that takes two arguments. It will first pass the arguments to the predicate function in the order it received them. If it returns a `true`

, the function will return `-1`

. Otherwise, it will check the value in the predicate function again, but this time with arguments switched. Once again, if the value is `true`

, the function will return `1`

. If neither function calls return `true`

, it will return `0`

.

So let's modernize it. In a previous post, I mention that I have two rules of function styles:

- I prefer the traditional syntax for top-level, named functions.
- I prefer the arrow syntax for anonymous functions, with implicit returns if the return is simple

The other thing that I prefer, is to avoid the `else`

statement in my if-blocks. Given those two things, we can refactor our `comparator`

function like this:

```
function comparator(pred) {
return (x, y) => {
if (pred(x, y)) return -1;
if (pred(y, x)) return 1;
return 0;
};
}
[5, 1, 2, 8].sort(comparator((a, b) => a < b));
// [1, 2, 5, 8]
[5, "1", "2", 8].sort(comparator(isString));
// ["2", "1", 5, 8]
```

Now let's add types. First of all, I think it is worth our time to define a `PredicateFunction`

type and a `ComparatorFunction`

type:

```
type PredicateFunction = (x: unknown, y: unknown) => boolean;
type ComparatorFunction = (x: unknown, y: unknown) => 1 | -1 | 0;
```

In the above, we state that both functions take two unknown values and will either return a boolean if it is of a `PredicateFunction`

type or `1 | -1 | 0`

if it is of a `ComparatorFunction`

type. One could argue that a `ComparatorFunction`

type only needs to return a `number`

, but I would prefer to be more specific if that is possible.

So now the only thing we need to do is to add that to our function signature, like this:

```
function comparator(pred: PredicateFunction): ComparatorFunction {
return (x, y) => {
if (pred(x, y)) return -1;
if (pred(y, x)) return 1;
return 0;
};
}
[5, 1, 2, 8].sort(comparator((a, b) => a < b));
// [1, 2, 5, 8]
[5, "1", "2", 8].sort(comparator(isString));
// ["2", "1", 5, 8]
```

Luckily, nothing else is needed. All other types can be inferred from these two types added to the function signature. We have a nice type-safe utility function that takes in a predicate function and returns a comparator function.