JavaScript Array Sort - In Depth (and some handy sort utilities)
Tags: javascript
Published: 2020-10-02 17:20:48


This post describes JavaScript's array sort method with examples of arrays of values, arrays of objects and sorting an object by key.

### Simple Examples

`Array.prototype.sort()` sorts an array in place (i.e., it mutates the original array).

To sort numbers:

```javascript
const numbers = [4, 8, 2, 0]
numbers.sort()
console.log(numbers) // -> [0, 2, 4, 8]
```

To sort strings:

```javascript
const strings = ['c', 'd', 'b', 'a']
strings.sort()
console.log(strings) // -> ["a", "b", "c", "d"]
```

To sort objects you need to use a custom sort function.

The equivalent sort function for the 2 examples above is:

```javascript
function defaultSort(a, b) {
  if (a < b) {
    return -1 // A negative result moves `a` before `b` to a lower
              // array index.
  }

  if (b < a) {
    return 1 // A positive result moves `a` after `b` to a higher
             // array index.
  }

  return 0 // A zero result leaves 'a' and 'b' in the same order,
           // relative to each other
           // (other array items might get sorted above them)
}
```

To use this sort function:

```javascript
const numbers = [4, 8, 2, 0]
numbers.sort(defaultSort)
console.log(numbers) // -> [0, 2, 4, 8]
```

With es6 you can simplify this sort function:

```javascript
const numbers = [4, 8, 2, 0]
numbers.sort((a, b) => a - b)
console.log(numbers) // -> [0, 2, 4, 8]
```

### Sorting an Array of Objects by Property

Here is the array of objects we will be working with:

```javascript
const employees = [
	{
		name: 'Jane',
		salary: 20000,
		role: 'Programmer'
	},
	{
		name: 'Dave',
		salary: 25000,
		role: 'Programmer'
	},
	{
		name: 'Ben',
		salary: 10000,
		role: 'Programmer'
	},
	{
		name: 'Carol',
		salary: 50000,
		role: 'Manager'
	},
	{
		name: 'Bob',
		salary: 25000,
		role: 'Programmer'
	},
	{
		name: 'Alice',
		salary: 15000,
		role: 'Programmer'
	},
	{
		name: 'Carol',
		salary: 100000,
		role: 'CEO'
	},
	{
		name: 'Dave',
		salary: 20000,
		role: 'Programmer'
	}
]
```

You sort objects by property:

```javascript
function sortByName({name: a}, {name: b}) {
  if (a < b) {
    return -1
  }

  if (b < a) {
    return 1
  }

  return 0
}

employees.sort(sortByName)
```

You can generalise this to work for any properties:

```javascript
const sortByProp = (prop) => (a, b) => {
  if (a[prop] < b[prop]) {
    return -1
  }

  if (b[prop] < a[prop]) {
    return 1
  }

  return 0
}
```

It can be used like this:

```javascript
const sortByName = sortByProp('name')

employees.sort(sortByName)
```

### Sorting Objects by Multiple Properties

The trick is to apply subsequent sort criteria only when earlier sort criteria return '0', i.e., the previous sort criteria considered the items to be equal.

This can be done with a single sort function, but I find it much easier to use an `order` function to compose the sort functions:

```javascript
const order = (...fns) => (...args) => {
	let res = 0, i = 0;
	while (
		fns[i] !== undefined &&
		(res = fns[i++](...args)) === 0
	);

	return res
}

const byName = sortByProp('name')
const bySalary = sortByProp('salary')
const byRole = sortByProp('role')

employees.sort(
  order(
    byName,
    bySalary,
    byRole,
  )
)

console.log(employees)
```

This code reads much easier and, if you wanted to change the order of the sort criteria, you simply swap the order of the functions:

```javascript
employees.sort(
  order(
    byRole,
    bySalary,
    byName,
  )
)
```

An added benefit is it makes it much easier to write individual unit tests for each tiny sort function!

## Sorting Objects by Key

Finally, to sort an object by keys you use the `Object.entries` and `Object.fromEntries` methods.

```javascript
const fruits = {
  'oranges': {
    cost: .8,
    quantity: 3
  },
  'bananas': {
    cost: .75,
    quantity: 5
  },
  'apples': {
    cost: .5,
    quantity: 10
  }
}

const sorted = Object.fromEntries(
  Object.entries(
    fruits
  ).sort(([key]) => key)
)

console.log(sorted)
```

How does this work?

1. `Object.entries(fruits)` returns an array of arrays. Each sub array contains two elements: `key` and `object`:

```javascript
[
  ["oranges", {
    cost: 0.8,
    quantity: 3
  }], ["bananas", {
    cost: 0.75,
    quantity: 5
  }], ["apples", {
    cost: 0.5,
    quantity: 10
  }]
]
```

2. `.sort(([key]) => key)` uses an es6 arrow function and destructuring to sort each array by the first element: `key`

3. `Object.fromEntries` converts the "array of arrays" back to an object.