Sorting Slices
sort.Slice ยท sort.SliceStablesort.Slice is the everyday workhorse. Pass the slice and a less function that returns true when element i should come before element j. It uses an unstable sort โ equal elements may be reordered.
sort.Slice โ sort any slice with a comparator
sort.Slice
nums := []int{5, 2, 8, 1, 9, 3} sort.Slice(nums, func(i, j int) bool { return nums[i] < nums[j] // ascending }) // [1 2 3 5 8 9] // Descending โ flip the comparison sort.Slice(nums, func(i, j int) bool { return nums[i] > nums[j] }) // [9 8 5 3 2 1] words := []string{"banana", "apple", "cherry"} sort.Slice(words, func(i, j int) bool { return words[i] < words[j] }) // [apple banana cherry]
Sorting structs by a field
Structs
type Person struct { Name string Age int } people := []Person{ {"Alice", 30}, {"Bob", 25}, {"Charlie", 35}, {"Diana", 25}, } // Sort by Age ascending sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age }) // [{Bob 25} {Diana 25} {Alice 30} {Charlie 35}] // (Bob and Diana order is not guaranteed โ use SliceStable for that) // Sort by Name alphabetically sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name }) // [{Alice 30} {Bob 25} {Charlie 35} {Diana 25}]
sort.SliceStable โ preserve order of equal elements
Stable
// SliceStable guarantees that equal elements keep their original relative order // Useful when sorting by one field but preserving a prior sort on another // Step 1: sort by Name sort.SliceStable(people, func(i, j int) bool { return people[i].Name < people[j].Name }) // Step 2: sort by Age โ Name order is preserved within equal ages sort.SliceStable(people, func(i, j int) bool { return people[i].Age < people[j].Age }) // [{Bob 25} {Diana 25} {Alice 30} {Charlie 35}] // Bob comes before Diana (alphabetical) because SliceStable preserved it
Built-in Convenience Functions
Ints ยท Strings ยท Float64sFor the three most common types there are pre-built functions so you don't need to write a comparator. Each also has a corresponding
AreSorted check and Search variant.
sort.Ints & sort.Strings
Convenience
nums := []int{3, 1, 4, 1, 5, 9} sort.Ints(nums) // [1 1 3 4 5 9] words := []string{"go", "rust", "c"} sort.Strings(words) // [c go rust] floats := []float64{3.14, 1.41, 2.71} sort.Float64s(floats) // [1.41 2.71 3.14]
IntsAreSorted & StringsAreSorted
Check
nums := []int{1, 2, 3, 5} sort.IntsAreSorted(nums) // true unsorted := []int{3, 1, 2} sort.IntsAreSorted(unsorted) // false words := []string{"a", "b", "c"} sort.StringsAreSorted(words) // true // Generic version โ works with any less function sort.SliceIsSorted(nums, func(i, j int) bool { return nums[i] < nums[j] }) // true
Multi-Key Sorting
Primary ยท Secondary ยท Tiebreak
Sorting by multiple fields in one pass
Multi-Key
type Employee struct { Dept string Name string Salary int } employees := []Employee{ {"Eng", "Zara", 90000}, {"Eng", "Alice", 95000}, {"HR", "Bob", 70000}, {"Eng", "Alice", 88000}, } // Primary: Dept asc Secondary: Name asc Tertiary: Salary desc sort.Slice(employees, func(i, j int) bool { a, b := employees[i], employees[j] if a.Dept != b.Dept { return a.Dept < b.Dept } if a.Name != b.Name { return a.Name < b.Name } return a.Salary > b.Salary // highest salary first within same name }) // [{Eng Alice 95000} {Eng Alice 88000} {Eng Zara 90000} {HR Bob 70000}]
sort.Interface
Len ยท Less ยท SwapImplement
sort.Interface on your own type to use sort.Sort, sort.Stable, and sort.Reverse. This is the older, more verbose approach โ prefer sort.Slice for simple cases.
Implementing sort.Interface
Interface
type ByLength []string func (b ByLength) Len() int { return len(b) } func (b ByLength) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b ByLength) Less(i, j int) bool { return len(b[i]) < len(b[j]) } words := ByLength{"peach", "kiwi", "pineapple", "fig"} sort.Sort(words) // [fig kiwi peach pineapple] // Stable version โ preserves original order for equal lengths sort.Stable(words)
sort.Reverse โ wrap any Interface to flip the order
Reverse
// sort.Reverse wraps any sort.Interface to sort in reverse words := ByLength{"peach", "kiwi", "pineapple", "fig"} sort.Sort(sort.Reverse(words)) // [pineapple peach kiwi fig] // Also works with the convenience types nums := sort.IntSlice{3, 1, 4, 1, 5} sort.Sort(sort.Reverse(nums)) // [5 4 3 1 1] // For sort.Slice, just flip the less function instead sort.Slice(nums, func(i, j int) bool { return nums[i] > nums[j] })
sort.IntSlice, sort.StringSlice โ ready-made Interface types
Convenience Types
// sort.IntSlice, sort.StringSlice, sort.Float64Slice // implement sort.Interface so you can use sort.Search on them nums := sort.IntSlice{1, 3, 5, 7, 9} // Search within a sorted IntSlice i := nums.Search(5) // 2 โ index of 5 i = nums.Search(4) // 2 โ insertion point for 4 // You can call Sort directly on the value s := sort.StringSlice{"banana", "apple", "cherry"} s.Sort() // [apple banana cherry]
Binary Search
sort.Search ยท SearchIntssort.Search requires the slice to already be sorted. It returns the smallest index i in [0, n) for which f(i) is true. If no such index exists it returns n.
sort.Search โ generic binary search
sort.Search
nums := []int{1, 3, 5, 7, 9, 11} target := 7 // Find index of target in a sorted ascending slice i := sort.Search(len(nums), func(i int) bool { return nums[i] >= target }) if i < len(nums) && nums[i] == target { fmt.Printf("found %d at index %d\n", target, i) // found 7 at index 3 } else { fmt.Println("not found") } // Find the insertion point for a new value insert := sort.Search(len(nums), func(i int) bool { return nums[i] >= 6 }) // insert = 3 (6 would go between 5 and 7)
SearchInts / SearchStrings
Convenience
nums := []int{2, 4, 6, 8, 10} // Returns the index; check if nums[i] == target i := sort.SearchInts(nums, 6) // i = 2 i = sort.SearchInts(nums, 5) // i = 2 (insertion point for 5) words := []string{"apple", "banana", "cherry"} j := sort.SearchStrings(words, "banana") // j = 1
Searching a descending slice
Descending
// For a descending slice, flip the comparison desc := []int{10, 8, 6, 4, 2} target := 6 i := sort.Search(len(desc), func(i int) bool { return desc[i] <= target // <= for descending }) if i < len(desc) && desc[i] == target { fmt.Println("found at index", i) // found at index 2 }
sort vs. slices package
Go 1.21+ genericsGo 1.21 added the
slices package with generic sorting functions. They're often more ergonomic for typed slices. Both packages coexist โ use whichever reads clearer in context.
sort package (pre-1.21 style)
sort
nums := []int{3, 1, 4, 1, 5} sort.Ints(nums) sort.Slice(nums, func(i, j int) bool { return nums[i] < nums[j] }) i := sort.SearchInts(nums, 4)
slices package (Go 1.21+, generic)
Go 1.21+
nums := []int{3, 1, 4, 1, 5} slices.Sort(nums) // type-inferred slices.SortFunc(nums, func(a, b int) int { return cmp.Compare(a, b) // returns -1/0/1 }) i, _ := slices.BinarySearch(nums, 4)
When to prefer slices.SortFunc
Guidance
import ( "cmp" "slices" ) type Person struct{ Name string; Age int } people := []Person{{"Alice", 30}, {"Bob", 25}, {"Carol", 30}} // slices.SortFunc takes a cmp function (returns int) not a less function (returns bool) // Use cmp.Compare for ordered types; chain with || 0 for multi-key slices.SortFunc(people, func(a, b Person) int { if n := cmp.Compare(a.Age, b.Age); n != 0 { return n } return cmp.Compare(a.Name, b.Name) }) // [{Bob 25} {Alice 30} {Carol 30}]
Quick Reference
All key functions| Function | Signature (simplified) | Description |
|---|---|---|
| sort.Slice | (slice any, less func(i,j int) bool) | Sort any slice in-place; unstable |
| sort.SliceStable | (slice any, less func(i,j int) bool) | Stable sort; preserves equal element order |
| sort.SliceIsSorted | (slice any, less func) bool | Report whether slice is sorted by less |
| sort.Ints | (a []int) | Sort int slice ascending |
| sort.Strings | (a []string) | Sort string slice ascending |
| sort.Float64s | (a []float64) | Sort float64 slice ascending |
| sort.IntsAreSorted | (a []int) bool | True if int slice is ascending |
| sort.StringsAreSorted | (a []string) bool | True if string slice is ascending |
| sort.Sort | (data Interface) | Sort any type implementing sort.Interface |
| sort.Stable | (data Interface) | Stable sort on sort.Interface |
| sort.Reverse | (data Interface) Interface | Return reversed view of sort.Interface |
| sort.IsSorted | (data Interface) bool | True if data is in sorted order |
| sort.Search | (n int, f func(int) bool) int | Binary search: smallest i where f(i) is true |
| sort.SearchInts | (a []int, x int) int | Binary search for x in sorted int slice |
| sort.SearchStrings | (a []string, x string) int | Binary search for x in sorted string slice |
| sort.SearchFloat64s | (a []float64, x float64) int | Binary search for x in sorted float64 slice |