Skip to main content

Ordering a Collection

Sorting means putting the elements of a collection in a certain order.

Some types have a natural order. Numbers are ordered by value: 1 comes before 2. Strings are ordered alphabetically: "four" comes before "one". For everything else, or when you want a different order, you give a custom order: a compare function that looks at two elements and says which one comes first.

Go sorts slices with the slices package:

  • slices.Sort() sorts a slice in natural order. It changes the slice in place: the original order is gone.
  • slices.Sorted() (Go 1.23) returns a new sorted slice and leaves the original as it was. You pass it slices.Values(s), which walks through the elements of s.
  • slices.SortFunc() sorts in place using your own compare function.

The older sort.Slice() from the sort package does the same job with a "less" function that returns true or false. New code usually uses slices.

The examples below use the words one, two, three, four.

SortFunc compare function syntax

T stands for the element type. The compare function gets two elements and returns a number: negative when a comes first, positive when b comes first, and 0 when they are equal. cmp.Compare(a, b) from the cmp package gives exactly such a number for numbers and strings.

main.go
slices.SortFunc(items, func(a, b T) int {
  // return a negative number if a comes first,
  // a positive number if b comes first, 0 if equal
})

Natural Order

Natural order is the order a type has on its own: numbers by value, strings letter by letter.

slices.Sorted(slices.Values(s)) gives a new slice in natural order. For descending order there is no separate function: sort first, then turn the slice around with slices.Reverse().

Your own types have no natural order. In Go you add one by writing a Compare method on the struct that returns a negative number, zero or a positive number, the same rule as a compare function. Then you can pass that method to slices.SortFunc() or use it directly to compare two values.

Sorted copy

slices.Sorted() makes a new, alphabetically sorted slice. The original words slice is printed afterwards to show it did not change.

Prompt
Take the words one, two, three, four. Make a new slice sorted in natural (alphabetical) order with slices.Sorted, so the original slice is not changed. Print the sorted slice, then print the original slice.
Output: Go program and captured output
Sorted copy

Sorted descending

The copy is sorted, then slices.Reverse() flips it, so the words go from "two" down to "four".

Prompt
Take the words one, two, three, four. Make a sorted copy with slices.Sorted, then turn it around with slices.Reverse so the words are in descending alphabetical order. Print the result.
Output: Go program and captured output
Sorted descending

A type with its own order

Version has a Major and a Minor number. Its Compare method compares Major first; only when they are equal does Minor decide. cmp.Or() picks the first result that is not 0. Checking Compare(...) > 0 answers "is the first version greater?".

Prompt
Define a struct Version with Major and Minor fields and give it a Compare method that returns a negative number, zero or a positive number, comparing Major first and then Minor. Print whether version 1.2 is greater than 1.3, then whether 2.0 is greater than 1.5, using Compare(...) > 0.
Output: Go program and captured output
A type with its own order

Custom Orders

A custom order is any order you describe with a compare function.

slices.SortFunc() sorts a slice in place with that function. If two elements compare as equal, their order is not guaranteed. slices.SortStableFunc() keeps equal elements in their original order. Use it when that matters, for example when sorting words by length.

A common wish is to sort by one property of each element. Go has no special function for that: you write a compare function that compares that property, usually with cmp.Compare().

For descending order, swap a and b in the comparison: cmp.Compare(b, a) instead of cmp.Compare(a, b).

To sort by one thing and then by another, combine two comparisons with cmp.Or(). It returns the first result that is not 0. If the first comparison says the elements are equal, the second one breaks the tie.

Sort with a compare function

The compare function returns len(a) - len(b). A negative result means a is shorter and comes first, so the words end up from shortest to longest.

Prompt
Take the words aaa, bb, c. Sort them with slices.SortFunc using a compare function that returns len(a) - len(b), so shorter words come first. Print the result.
Output: Go program and captured output
Sort with a compare function

Sort by length

The words are compared by length with cmp.Compare(len(a), len(b)). "one" and "two" are both 3 letters long; the stable sort keeps "one" before "two".

Prompt
Take the words one, two, three, four. Sort them by length with slices.SortStableFunc and cmp.Compare on the lengths, so words of the same length keep their original order. Print the result.
Output: Go program and captured output
Sort by length

Sort by last letter, descending

a[len(a)-1] is the last letter of a word. The letters of b and a are passed to cmp.Compare() in swapped order, so the word with the largest last letter comes first: "four" (r), then "two" (o), then the two words ending in "e" in their original order.

Prompt
Take the words one, two, three, four. Sort them by their last letter in descending order: use slices.SortStableFunc with cmp.Compare, passing the last letter of b before the last letter of a. Print the result.
Output: Go program and captured output
Sort by last letter, descending

Sort by length, then alphabetically

cmp.Or() first compares the lengths. When two words are equally long, it falls back to strings.Compare(), which orders them alphabetically.

Prompt
Take the words aaa, bb, c, b, a, aa, ccc. Sort them first by length and then alphabetically, using slices.SortFunc with cmp.Or to combine cmp.Compare on the lengths and strings.Compare on the words. Print the result.
Output: Go program and captured output
Sort by length, then alphabetically

Check Sorted Order

Sometimes you only want to know whether a slice is already sorted, without sorting it.

slices.IsSorted() returns true when the slice is in ascending natural order. slices.IsSortedFunc() does the same with your own compare function. Go has no IsSortedDescending: pass a compare function with a and b swapped.

To check the order of one property, such as a user's age, compare that property inside the function.

Ascending and descending check

slices.IsSorted() confirms that 1, 2, 3, 4 are ascending. slices.IsSortedFunc() with cmp.Compare(b, a) confirms that 4, 3, 2, 1 are descending.

Prompt
Check whether the numbers 1, 2, 3, 4 are in ascending order with slices.IsSorted and print the answer. Then check whether 4, 3, 2, 1 are in descending order with slices.IsSortedFunc and a compare function that swaps a and b, and print that answer.
Output: Go program and captured output
Ascending and descending check

Check order by a field

The users are Alice (24), Bob (31) and Charlie (29). The compare function looks only at Age. Bob is older than Charlie, so the slice is not sorted by age.

Prompt
Define a struct User with Name and Age. Make a slice with Alice 24, Bob 31 and Charlie 29. Use slices.IsSortedFunc with cmp.Compare on the ages to check whether the users are sorted by age, and print the answer.
Output: Go program and captured output
Check order by a field

Reverse Order

Reversing puts the last element first and the first element last.

slices.Reverse() reverses a slice in place. To get a reversed copy instead, first copy the slice with slices.Clone() and reverse the copy. The original keeps its order.

Reversed copy

The copy is reversed and printed, then the original is printed to show it is unchanged.

Prompt
Take the words one, two, three, four. Copy the slice with slices.Clone and reverse the copy with slices.Reverse, so the original stays the same. Print the reversed copy, then print the original.
Output: Go program and captured output
Reversed copy

Random Order

Shuffling puts the elements in a random order.

Go's math/rand package has a Shuffle() function. You give it the number of elements and a swap function; it calls the swap function for random pairs of positions. Like slices.Reverse(), it works in place, so shuffle a copy made with slices.Clone() if you still need the original.

A random source made with rand.New(rand.NewSource(seed)) gives the same "random" order every time the program runs with that seed. This is useful for tests and for examples like this one. Without a fixed seed the order changes on every run.

Shuffled copy

A random source with seed 7 shuffles a copy of the words. The swap function exchanges the elements at positions i and j.

Prompt
Take the words one, two, three, four. Create a random source with seed 7 using rand.New(rand.NewSource(7)). Shuffle a copy of the slice made with slices.Clone, using the Shuffle method with a swap function. Print the shuffled copy.
Output: Go program and captured output
Shuffled copy