# Dynamic Sort Direction

This is a follow-up to my "Multisort via Composition" post, and won't make sense if you haven't read that post.

When sorting on a single dimension, changing the sort direction can usually be handled by changing the sorter function directly.

```
const data = [33, 1, 95, 77, 54, 91, 38, 89, 48, 24];
// ascending
data.sort((a, b) => a - b);
// descending
data.sort((a, b) => b - a);
```

making this act dynamically based on some external input can be reasonably handled by updating the function as well:

```
const ascending = getAscending(); // true or false
data.sort((a, b) => (ascending ? a - b : b - a));
```

This works fine for a single sorter, but quickly becomes untenable once multiple sorting dimensions are used:

```
const data = [
{
first: "Emma",
last: "Smith",
age: 33,
},
{
first: "Noah",
last: "Johnson",
age: 1,
},
{
first: "Olivia",
last: "Williams",
age: 95,
},
{
first: "Liam",
last: "Jones",
age: 77,
},
{
first: "Sophia",
last: "Brown",
age: 54,
},
{
first: "Mason",
last: "Davis",
age: 91,
},
{
first: "Ava",
last: "Miller",
age: 38,
},
{
first: "Jacob",
last: "Smith",
age: 89,
},
{
first: "William",
last: "Moore",
age: 48,
},
{
first: "Isabella",
last: "Johnson",
age: 24,
},
];
data.sort(
multisort(
(a, b) =>
lastNameAscending
? a.last.localeCompare(b.last)
: b.last.localeCompare(a.last),
(a, b) =>
firstNameAscending
? a.first.localeCompare(b.first)
: b.first.localeCompare(a.first)
)
);
```

Once `multisort`

is used, it's more convenient to just pass sorter functions around, particularly if they can be passed from an external source.

A simple way to handle dynamic direction is to toggle which sorter is used:

```
const byLastNameAscending = (a, b) => a.last.localeCompare(b.last)
const byLastNameDescending = (a, b) => b.last.localeCompare(a.last)
const byLastName =
lastNameAscending
? byLastNameAscending
: byLastNameDescending
...
data.sort(multisort(
byLastName,
byFirstName
))
```

This form still has issues in that it requires declaring two nearly identical sort functions, which could cause issues if an update is made to one and not the other.

A better way to handle reversing a sorter function is to use a reverser function that wraps a given sort function and reverses the arguments:

`const reverseSort = (sort) => (a, b) => sort(b, a);`

This at least avoids the need for re-declaring a given (possibly complex) sorter:

```
const byLastNameAscending = (a, b) => a.last.localeCompare(b.last)
const byLastNameDescending = reverseSort(byLastNameAscending)
const byLastName =
lastNameAscending
? byLastNameAscending
: byLastNameDescending
...
data.sort(multisort(
byLastName,
byFirstName
))
```

Generally speaking this will work quite well for a dynamic sort with minimal overhead.

As an aside, it can be useful to use two declarative functions for static sorting:

```
const ascending = (sort) => (a, b) => sort(a, b); //*
const descending = (sort) => (a, b) => sort(b, a);
data.sort(multisort(ascending(byLastName), descending(byFirstName)));
```

_{* this can be replaced by the identity function ascending = sort => sort, but I find the symmetry to be useful for comprehension.}

It's possible to abstract the dynamic sort direction even further.

Instead of using a ternary directly to choose a particular sort function, a `sortDirection`

function can be used to generate the appropriate function based on the input of the sorting function and the direction:

```
const sortDirection = (sort, ascending) =>
ascending ? (a, b) => sort(a, b) : (a, b) => sort(b, a);
data.sort(
multisort(
sortDirection(byLastName, lastNameAscending),
sortDirection(byFirstName, firstNameAscending)
)
);
```