Sorting built-in types
slices.Sort sorts any slice whose element type is ordered: integers, floats and strings. It sorts in place and returns nothing.
Output:
[3 7 19 42 88]
[Bob Carl alice lisa]
true
2 true
Strings sort by bytes, so every uppercase ASCII letter comes before every lowercase one. That is rarely what a user expects for names. The next section fixes it.
The slices package arrived in Go 1.21. Its Sort is a pattern-defeating quicksort: O(n log n), in place, and not stable.
Custom order with SortFunc
slices.SortFunc takes a comparison function func(a, b T) int. Return a negative number when a should come first, a positive number when b should, and zero when they are equal. The cmp.Compare helper returns exactly that for ordered types.
strings.ToLower inside a comparison allocates a new string whenever its input has an uppercase letter, and the comparison runs about n log n times. For big slices, compute the lowercase keys once. For names in other languages (accents, locale rules), use golang.org/x/text/collate, which is outside the standard library.
A comparison function must be consistent: if it says a comes before b, it must say b comes after a. One that breaks this (for example, returning -1 whenever two values differ) produces a wrongly ordered slice with no error. Subtracting integers (return a - b) looks neat but overflows for large values; use cmp.Compare.
Sorting structs
The comparison function receives elements, so sorting structs by a field is the same call:
Employees with equal salaries may come out in any order here, because SortFunc is not stable. If you need a guaranteed order, either break ties with more fields or use a stable sort.
Sorting by multiple fields
Compare the most important field first and move to the next only on a tie. cmp.Or (Go 1.22) returns its first non-zero argument, which turns this into one line:
Output:
eng 150 Cy
eng 120 Ana
eng 120 Eve
sales 90 Bob
sales 90 Dee
Every comparison is evaluated even when the first one decides, since they are plain arguments. That costs little for field comparisons. When a tie-breaker is expensive, write the if c := ...; c != 0 { return c } chain by hand.
Stable sort
A stable sort keeps elements that compare equal in their original order. That matters when the input already has a meaningful order, for example records sorted by time that you now group by user.
This prints [{ana 2} {ana 4} {bob 1} {bob 3} {bob 5}]: within each user, the original sequence survives. Stable sorting does more work, so use it only when the order of equal elements matters.
Sorting a map
Maps have no order. To show a map sorted by key, sort its keys: slices.Sorted(maps.Keys(m)) (Go 1.23). To sort by value, sort the keys with a comparison that looks up the values:
The tie-breaker on the word matters: without it, chan and slice (both 7) would print in a different order from run to run, because the keys come out of the map in random order. See maps for more.
The sort package: sort.Slice and friends
Before Go 1.21, sorting went through the sort package. You will see it in a lot of existing code:
sort.Ints(nums)
sort.Strings(names)
sort.Slice(staff, func(i, j int) bool {
return staff[i].Salary < staff[j].Salary
})
sort.SliceStable(staff, func(i, j int) bool { ... })
Differences worth knowing:
sort.Slice | slices.SortFunc | |
|---|---|---|
| Function receives | indexes i, j | elements a, b |
| Returns | bool (is i less than j) | int (negative, zero, positive) |
| Type safety | takes any, uses reflection | generic, checked at compile time |
| Speed | slower | faster |
A common sort.Slice bug is closing over a different slice than the one being sorted, since the less function indexes by position. SortFunc cannot have that bug because it hands you the elements.
The sort.Interface type (Len, Less, Swap) is the oldest form. It is still the way to sort data that is not a single slice, such as two parallel slices that must move together. Since Go 1.22, sort.Ints, sort.Strings and sort.Float64s simply call slices.Sort.
Common mistakes
- Expecting
Sortto return the sorted slice. It sorts in place and returns nothing. Useslices.Sorted(slices.Values(s))if you want a new sorted slice, orslices.Clonefirst. - Assuming equal elements keep their order. Only the
Stablevariants promise that. - Subtracting to compare.
a - boverflows. Usecmp.Compare. - Sorting floats with NaN.
cmp.Compareorders NaN before every other value, which keeps the sort consistent. A hand-writtena < bcomparison does not.
Frequently Asked Questions
How do you sort a slice in Go?
For numbers and strings, call slices.Sort(s) (Go 1.21). It sorts in place in ascending order. For anything else, or a different order, use slices.SortFunc(s, func(a, b T) int { ... }), where the function returns a negative number if a comes first, positive if b does, and zero if they are equal.
How do I sort a slice in descending order in Go?
Swap the arguments in the comparison: slices.SortFunc(s, func(a, b int) int { return cmp.Compare(b, a) }). Or sort ascending and then call slices.Reverse(s).
How do I sort a slice of structs by multiple fields in Go?
Compare the first field, and fall through to the next only when it is equal. cmp.Or (Go 1.22) does exactly that: return cmp.Or(cmp.Compare(a.Dept, b.Dept), cmp.Compare(b.Salary, a.Salary), strings.Compare(a.Name, b.Name)) returns the first non-zero result.
What is the difference between sort.Slice and slices.SortFunc?
sort.Slice(s, func(i, j int) bool) is the older API: it takes a less function over indexes and uses reflection to swap elements. slices.SortFunc(s, func(a, b T) int) is generic, type-checked, takes the elements directly, and is faster. New code should use the slices package; sort.Slice is still common in code written before Go 1.21.