If you have written some JavaScript and manipulate slightly complex data, you have had to write some code like this to sort an array of objects:

``````const data = [
{ name: "Alice", age: 22 },
{ name: "Bob", age: 32 },
{ name: "Carl", age: 63 },
{ name: "Clara", age: 28 },
...
];

data.sort(function(a, b) {
if (a.name < b.name) {
return -1;
}

if (a.name > b.name) {
return 1;
}

return 0;
})

// Or, as a one-liner:
data.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
``````

While this is perfectly fine for one-off sorting of shallow objects, it can get a bit more complex and repetitive when having to sort based on nested fields.

Something else you might have tripped on while using the native `.sort()` on arrays, is the following behaviour:

``````const array = [1, 2, 3, 10, 23]
console.log(array.sort())
// [1, 10, 2, 23, 3]
``````

Indeed, by default, the comparison function used by `.sort()` treats each element as a string! To make the above example work, you need to pass a custom comparison function such as the following one-liner:

``````const array = [1, 23, 3, 10, 2]
console.log(array.sort((a, b) => a - b))
// [1, 2, 3, 10, 23]
``````

As sorting is a common operation on arrays, a more scalable and less error-prone strategy would be to define common compare functions. Let's build said compare functions!

First, let's look at the API we would like to end up with:

``````const array = [1, 23, 3, 10, 2]
array.sort(numerically)
// Should be equivalent to:
array.sort((a, b) => a - b)

array.sort(numerically.desc)
// Should be equivalent to:
array.sort((a, b) => b - a)
// For completeness, we can also expose `numerically.asc`.
``````

To achieve the above API, we can define `numerically` as follows:

``````function numerically (a, b) {
return a - b;
}
``````

As in JavaScript, (almost) everything is an object, we can then add a `desc` and an `asc` field to the `numerically` function as follows:

``````numerically.desc = function(a, b) {
return b - a;
}

numerically.asc = function(a, b) {
return numerically(a, b); // This works because we sort from lower to higher by default!
}
``````

Now that we have defined compare functions to work on arrays holding primitives values, let's generalise it to arrays of objects:

``````const data = [
{ name: "Alice", age: 22 },
{ name: "Bob", age: 32 },
{ name: "Carl", age: 63 },
{ name: "Clara", age: 28 },
...
];

data.sort(alphabetically.by("name"))
// Should be equivalent to:
data.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)
``````

To achieve that, let's create a small utility function that will help us retrieve the value of an object based on a key path:

``````function getValueByKey(obj, key) {
return String(key)
.split(".")
.reduce((acc, cur) => acc?.[cur] ?? null, obj);
}
``````

With the code above, we can do deep object look-ups!

With that in hand, let's add the following to our example `alphabetically` sort function:

``````function alphabetically (a, b) { ... }

alphabetically.desc = function(a, b) { ... }
alphabetically.asc = function(a, b) { ...}

alphabetically.by = function(key) {
return function(a, b) {
const aVal = getValueByKey(a, key);
const bVal = getValueByKey(b, key);

return a < b ? -1 : a > b ? 1 : 0;
}
}
``````

Alright, this works great for ascending order sorting, but how could we implement descending order? There are different ways of solving this:

• Pass another argument that can have either `"desc"` or`"asc"` values (defaults to `"asc"`)
• Append a `-` sign in the key (for example: `sort(alphabetically.by("-name")`)
• Add `.desc()` and `.asc()` functions to our new function `.by()`

Either designs are fine, but to stay consistent with our previous utility function, we will be adding the ordering feature as follow:

``````data.sort(alphabetically.by("name").desc)
``````

All implemented, it looks like:

``````function alphabetically (a, b, direction = 1) {
if (a < b) {
return -1 * direction;
}

if (a > b) {
return 1 * direction;
}

return 0;
}

alphabetically.asc = (a, b) => alphabetically(a, b, 1);
alphabetically.desc = (a, b) => alphabetically(a, b, -1);

alphabetically.by = function(key) {
function compareBy(a, b, direction = 1) {
const aVal = getValueByKey(a, key);
const bVal = getValueByKey(b, key);

return aVal < bVal ? -1 * direction : aVal > bVal ? 1 * direction : 0;
}

compareBy.asc = (a, b) => compareBy(a, b, 1);
compareBy.desc = (a, b) => compareBy(a, b, -1);

return compareBy;
}
``````

I found this exercise particularly interesting, and decided to build a library with some of the ideas discussed in this post. You can have a look at it here:

https://github.com/AntonioVdlC/sort