Sorting (sort package)
Reviewed & published by Brayan K
Go's sort package and the newer slices.Sort family let you order any slice in place — from one-line helpers like sort.Ints to custom comparisons over struct fields.
Part of the free Go course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- Sort with sort.Ints and sort.Strings
- Sort anything with sort.Slice and a less func
- Keep ties in order with sort.SliceStable
- Sort structs by a field (and break ties)
- Use slices.Sort and slices.SortFunc (1.21+)
- Binary search a sorted slice with sort.Search
1️⃣ The Simple Helpers
For the common cases, the sort package has ready-made functions: sort.Ints and sort.Strings sort a slice in place, ascending. Once a slice is sorted, sort.Search (and wrappers like sort.SearchInts) can binary search it in O(log n), returning the index of the first element that satisfies your test.
package main
import (
"fmt"
"sort"
)
func main() {
// The simplest helpers sort in place, ascending.
nums := []int{3, 1, 4, 1, 5, 9, 2}
sort.Ints(nums)
fmt.Println(nums) // [1 1 2 3 4 5 9]
words := []string{"banana", "apple", "cherry"}
sort.Strings(words)
fmt.Println(words) // [apple banana cherry]
// sort.Search does binary search on a sorted slice and returns
// the index of the first element for which the test is true.
i := sort.SearchInts(nums, 4)
fmt.Println("first >= 4 at index", i, "value", nums[i])
}2️⃣ Custom Order with sort.Slice
To sort anything else, sort.Slice takes your slice plus a less func func(i, j int) bool that returns true when element i should come before element j. Use sort.SliceStable when equal elements must keep their original relative order — handy for multi-pass sorting where a previous ordering should survive within ties.
package main
import (
"fmt"
"sort"
)
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Cara", 36},
{"Ada", 36},
{"Dan", 22},
{"Ben", 22},
}
// First, sort by Name so ties have a known starting order.
sort.Slice(people, func(i, j int) bool {
return people[i].Name < people[j].Name
})
fmt.Println("by name: ", people)
// SliceStable by Age keeps the name order WITHIN each age group.
sort.SliceStable(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println("stable age:", people)
}3️⃣ slices.Sort & slices.SortFunc (Go 1.21+)
The generic slices package is the modern way. slices.Sort orders any slice of an ordered type, and slices.SortFunc takes a compare func returning a negative, zero, or positive number — cmp.Compare makes writing it trivial. Swap the two arguments to cmp.Compare to reverse the order.
package main
import (
"cmp"
"fmt"
"slices"
)
type Product struct {
Name string
Price int
}
func main() {
// slices.Sort works on any ordered element type (Go 1.21+).
xs := []int{5, 2, 8, 1}
slices.Sort(xs)
fmt.Println(xs) // [1 2 5 8]
// slices.SortFunc takes a compare func returning -1, 0, or +1.
// cmp.Compare makes that easy. Sort products by Price.
products := []Product{
{"pen", 3}, {"book", 12}, {"clip", 1},
}
slices.SortFunc(products, func(a, b Product) int {
return cmp.Compare(a.Price, b.Price)
})
fmt.Println(products)
// To reverse, swap the arguments to cmp.Compare.
slices.SortFunc(products, func(a, b Product) int {
return cmp.Compare(b.Price, a.Price)
})
fmt.Println("desc:", products)
}🎯 Your Turn
Sort the scores from highest to lowest by wrapping an IntSlice in sort.Reverse. Fill in the blank.
package main
import (
"fmt"
"sort"
)
func main() {
scores := []int{42, 7, 19, 99, 3}
// Sort DESCENDING by wrapping an IntSlice in sort.Reverse.
sort.Sort(sort.___(sort.IntSlice(scores))) // 👉 Reverse
fmt.Println(scores)
// ✅ Expected output: [99 42 19 7 3]
}Common Errors (and the fix)
- ❌ Expected a sorted return value — sort.Ints(xs) returns nothing. ✅ It sorts in place; just use xs afterward.
- ❌ less func used <= — that breaks the strict ordering and can misbehave. ✅ Use strict < (or > for descending).
- ❌ Ties got reordered unexpectedly — sort.Slice isn't stable. ✅ Use sort.SliceStable.
- ❌ sort.Search on an unsorted slice — results are meaningless. ✅ Sort first; binary search requires order.
🎯 Mini-Challenge: Sort by Two Keys
Sort the books by Year ascending, breaking ties alphabetically by Title. In the less func, compare Year first and fall through to Title only when the years are equal.
package main
import (
"fmt"
"sort"
)
type Book struct {
Title string
Year int
}
func main() {
// 🎯 MINI-CHALLENGE: sort by Year ascending, breaking ties by Title.
// Use sort.SliceStable with a less func that compares Year first,
// and falls back to Title when the years are equal. Then print
// "<year> <title>" for each book.
//
// ✅ Expected output:
// 2015 Go in Action
// 2015 The Go Programming Language
// 2021 Cloud Native Go
// 2021 Learning Go
books := []Book{
{"Go in Action", 2015},
{"The Go Programming Language", 2015},
{"Cloud Native Go", 2021},
{"Learning Go", 2021},
}
_ = sort.SliceStable
// your code here
}🎉 Lesson Complete!
- ✅ sort.Ints/sort.Strings sort in place, ascending
- ✅ sort.Slice sorts anything via a less(i, j) bool func
- ✅ sort.SliceStable preserves the order of equal elements
- ✅ slices.Sort/slices.SortFunc + cmp.Compare are the modern way
- ✅ sort.Search binary-searches a sorted slice in O(log n)
Practice quiz
What does sort.Ints(xs) return?
- A new sorted slice
- The smallest element
- Nothing — it sorts in place
- An error
Answer: Nothing — it sorts in place. sort.Ints sorts the slice in place, ascending, and returns nothing.
What must the less func passed to sort.Slice return?
- true when element i should come before element j
- -1, 0, or 1
- the larger of i and j
- the swapped slice
Answer: true when element i should come before element j. less(i, j) returns true when element i should sort before element j.
When should you use sort.SliceStable instead of sort.Slice?
- When the slice is large
- When sorting strings
- Never — they are identical
- When equal elements must keep their original relative order
Answer: When equal elements must keep their original relative order. Stable sorting preserves the order of elements that compare equal.
How do you sort a []int in descending order using the sort package helpers?
- sort.Ints then reverse the slice manually only
- sort.Sort(sort.Reverse(sort.IntSlice(xs)))
- sort.IntsDesc(xs)
- sort.Reverse(xs)
Answer: sort.Sort(sort.Reverse(sort.IntSlice(xs))). Wrap sort.IntSlice in sort.Reverse: sort.Sort(sort.Reverse(sort.IntSlice(xs))).
What does slices.SortFunc's compare func return?
- A negative, zero, or positive int
- A bool
- The sorted slice
- An index
Answer: A negative, zero, or positive int. SortFunc takes a compare returning <0, 0, or >0; cmp.Compare produces that.
How do you reverse the order with cmp.Compare in slices.SortFunc?
- Negate the slice
- Return -1 always
- Swap the arguments: cmp.Compare(b, a)
- Use slices.Reverse only
Answer: Swap the arguments: cmp.Compare(b, a). Swapping the arguments to cmp.Compare(b, a) reverses the ordering.
What does sort.SearchInts require of the slice and return?
- Unsorted slice; returns the max
- A sorted slice; returns the index of the first element >= target
- Any slice; returns true/false
- A sorted slice; returns the target value
Answer: A sorted slice; returns the index of the first element >= target. It binary-searches a sorted slice and returns the first index where the element is >= target.
What is the time complexity of sort.Search (binary search)?
- O(n)
- O(n log n)
- O(1)
- O(log n)
Answer: O(log n). Binary search runs in O(log n) but requires the slice to be sorted.
For a stable multi-key sort, how should the less func compare keys?
- Compare only the secondary key
- Compare the primary key first, fall through to the secondary only on ties
- Compare both keys with &&
- Order does not matter
Answer: Compare the primary key first, fall through to the secondary only on ties. Compare the primary key first and only break ties with the secondary key.
Why should a less func use strict < rather than <=?
- <= is a syntax error
- < is slower
- <= breaks the required strict weak ordering and can misbehave
- There is no difference
Answer: <= breaks the required strict weak ordering and can misbehave. The comparator must be a strict ordering; using <= can report both less(i,j) and less(j,i).
Continue this course
- Previous: Maps: sets, ordering & nested maps
- Next: Time & Dates (time package) — Durations, the reference layout 2006-01-02, parsing, and comparing times
Frequently asked questions
What is the difference between sort.Sort and sort.Slice?
sort.Sort needs a type that implements the sort.Interface (Len, Less, Swap), which is more ceremony. sort.Slice is the convenient modern form: you pass the slice plus an inline less(i, j) func and it does the rest. For almost all everyday sorting, reach for sort.Slice (or slices.SortFunc in Go 1.21+).
When should I use SliceStable instead of Slice?
Use sort.SliceStable when elements that compare equal must keep their original relative order. The plain sort.Slice is not guaranteed to be stable, so equal items can be reordered. Stable sorting matters when you sort by one key and want a previous ordering preserved within ties — for example sort by name, then stably by age.
How do I sort in descending order?
Two easy ways. With the less func, flip the comparison: return a[i] > a[j] instead of <. With the helper types, wrap them in sort.Reverse, e.g. sort.Sort(sort.Reverse(sort.IntSlice(xs))). In Go 1.21+, slices.SortFunc with cmp.Compare(b, a) (arguments swapped) also reverses the order.
What does the less function need to return?
less(i, j) returns true when the element at index i should come BEFORE the element at index j. It must define a strict weak ordering: never report both less(i,j) and less(j,i) as true. For multi-key sorts, compare the primary key first and only fall through to the secondary key when the primary values are equal.
What is sort.Search for?
sort.Search performs a binary search over a sorted range and returns the smallest index for which a test function is true (or len if none qualify). The convenience wrappers sort.SearchInts / sort.SearchStrings find the first element >= a target. Because it is O(log n), it is far faster than scanning, but the slice must already be sorted.