# 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)
))
```