The sort package
Go's standard library sorts slices with the sort package. The three functions you will use most are:
sort.Ints(slice)
sort.Strings(slice)
sort.Slice(slice, func(i, j int) bool { ... })
All of them sort in place: they rearrange the elements of the slice you pass and return nothing. If the original order is needed afterwards, copy the slice first.
Ascending and descending order
sort.Ints and sort.Strings sort in ascending order:
numbers := []int{11, 3, 7, 5, 2}
sort.Ints(numbers)
fmt.Println(numbers)
[2 3 5 7 11]
For descending order, wrap the slice in a type from the package and reverse it:
sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
fmt.Println(numbers)
[11 7 5 3 2]
Strings work the same way with sort.StringSlice. Strings compare by bytes, so every uppercase letter sorts before every lowercase one: sort.Strings on "Mircea", "john", "Andy" gives [Andy Mircea john].
Custom order with sort.Slice
sort.Slice takes a less function. It receives two indices, i and j, and returns true when the element at i must come before the element at j. It receives positions rather than values, so the function reads the elements from the slice itself:
numbers := []int{3, 23, 12}
sort.Slice(numbers, func(i, j int) bool { return numbers[i] < numbers[j] })
This gives [3 12 23]. The function is written inline, as an anonymous function, and it can use the variable numbers because closures capture variables from the surrounding code.
Sorting words by length, shortest first:
words := []string{"banana", "pie", "Washington", "book"}
sort.Slice(words, func(i, j int) bool { return len(words[i]) < len(words[j]) })
The result is [pie book banana Washington].
Sorting by two keys
When the first values are equal, break the tie with the second. Compare the first values for equality, then return the comparison of the second values; otherwise return the comparison of the first:
pairs := [][]int{{1, 5}, {12, 5}, {10, 1}, {1, 3}}
sort.Slice(pairs, func(i, j int) bool {
if pairs[i][0] == pairs[j][0] {
return pairs[i][1] < pairs[j][1]
}
return pairs[i][0] < pairs[j][0]
})
The result is [[1 3] [1 5] [10 1] [12 5]].
Gotchas
- The function gets indices. Writing
return i < jcompares positions, not values, and leaves the slice as it was. - Use a strict comparison. A less function must return
falsefor equal elements. Writing<=gives an inconsistent ordering and can produce wrong results. sort.Sliceis not stable. Elements that compare equal may end up in any relative order. Usesort.SliceStablewhen ties must keep their original order.- It sorts a slice, not an array value. Pass a slice; for a fixed-size array write
arr[:]. - It returns nothing.
numbers = sort.Ints(numbers)does not compile.
Checking and reusing a sort
To test whether a slice is already sorted, sort.IntsAreSorted(numbers) returns a boolean. After the descending sort above it returns false, because the function checks for ascending order. To sort by length from longest to shortest while keeping the original order of equal lengths, flip the comparison and use the stable variant: sort.SliceStable(words, func(i, j int) bool { return len(words[i]) > len(words[j]) }) gives [Washington banana book pie].
The same pattern works for any element type. For a slice of structs, the less function compares the fields you care about, and a tie on the first field falls through to the next one, exactly like the pairs example.
Complexity
Sorting n elements takes O(n log n * C) time, where C is the cost of one comparison. Comparing two integers is constant time, so sorting numbers is O(n log n). Comparing two strings costs up to their length L, so sorting strings is O(n log n * L). The sort happens in place and uses O(1) extra space in the sense the lesson cares about, since it builds no copy of the data.
The exercise, step by step
- Declare
numbersas the slice[]int{2, 1, 3, 10}with:=. - Sort it in descending order with
sort.Slice. The less function returnsnumbers[i] > numbers[j], so larger numbers come first. The result is[10 3 2 1]. - Declare
coordinatesas a[][]intwith three pairs,[]int{3, 2},[]int{2, 3}and[]int{3, 5}, one per line, each followed by a comma. Go requires the comma after the last element when the closing brace is on its own line. - Sort by the first value ascending, and by the second descending on ties. If the first values are equal, return
coordinates[i][1] > coordinates[j][1]; otherwise returncoordinates[i][0] < coordinates[j][0]. The result is[[2 3] [3 5] [3 2]]. - Declare
namesas[]string{"Mircea", "john", "Andy"}. - Sort alphabetically, ignoring case. The less function compares
strings.ToLower(names[i])withstrings.ToLower(names[j]). Without the lowercasing,johnwould sort last. The result is[Andy john Mircea].
numbers := []int{2, 1, 3, 10}
sort.Slice(numbers, func(i int, j int) bool {
return numbers[i] > numbers[j]
})
coordinates := [][]int{
[]int{3, 2},
[]int{2, 3},
[]int{3, 5},
}
sort.Slice(coordinates, func(i int, j int) bool {
if coordinates[i][0] == coordinates[j][0] {
return coordinates[i][1] > coordinates[j][1]
}
return coordinates[i][0] < coordinates[j][0]
})
names := []string{"Mircea", "john", "Andy"}
sort.Slice(names, func(i int, j int) bool {
return strings.ToLower(names[i]) < strings.ToLower(names[j])
})
The code uses the sort and strings packages, so a complete program imports both. An unused import is a compile error in Go.
Key takeaways
sort.Intsandsort.Stringssort ascending;sort.Slicesorts by any rule you write.- The less function receives indices and returns true when element
igoes before elementj. - Tie-breaking comes from checking equality on the first key before comparing the second.
- Sorting is in place, not stable by default (use
sort.SliceStable), and costsO(n log n)comparisons.