Golang slices.SortStableFunc
last modified April 20, 2025
This tutorial explains how to use the slices.SortStableFunc
function
in Go. We'll cover stable sorting with practical examples using custom comparison.
The slices.SortStableFunc function sorts a slice while preserving the original order of equal elements. It's part of Go's experimental slices package.
This function is useful when you need to maintain the relative order of equal elements after sorting. It accepts a custom comparison function for sorting.
Basic SortStableFunc Example
The simplest use of slices.SortStableFunc
sorts integers in
ascending order. We define a comparison function that returns -1, 0, or 1.
package main import ( "fmt" "slices" ) func main() { numbers := []int{3, 1, 4, 1, 5, 9, 2, 6} slices.SortStableFunc(numbers, func(a, b int) int { if a < b { return -1 } if a > b { return 1 } return 0 }) fmt.Println("Sorted numbers:", numbers) }
We sort a slice of integers while preserving the order of equal elements. The comparison function follows the standard less-than convention.
Sorting Strings by Length
slices.SortStableFunc
can sort strings by custom criteria. This
example sorts strings by length while keeping original order for equal lengths.
package main import ( "fmt" "slices" ) func main() { words := []string{"apple", "banana", "kiwi", "orange", "fig"} slices.SortStableFunc(words, func(a, b string) int { if len(a) < len(b) { return -1 } if len(a) > len(b) { return 1 } return 0 }) fmt.Println("Sorted by length:", words) }
The comparison function examines string lengths instead of lexicographical order. Original order is preserved for strings with equal lengths.
Sorting Structs by Multiple Fields
We can use slices.SortStableFunc
with custom struct types. This
example sorts people by age, then by name for equal ages.
package main import ( "fmt" "slices" ) type Person struct { Name string Age int } func main() { people := []Person{ {"Alice", 25}, {"Bob", 30}, {"Charlie", 25}, {"David", 30}, } slices.SortStableFunc(people, func(a, b Person) int { if a.Age < b.Age { return -1 } if a.Age > b.Age { return 1 } if a.Name < b.Name { return -1 } if a.Name > b.Name { return 1 } return 0 }) fmt.Println("Sorted people:") for _, p := range people { fmt.Printf("%s (%d)\n", p.Name, p.Age) } }
The comparison function first checks age, then name for equal ages. The stable sort preserves original order for people with identical age and name.
Case-Insensitive String Sorting
This example demonstrates case-insensitive sorting while preserving original order for strings that compare equal case-insensitively.
package main import ( "fmt" "slices" "strings" ) func main() { words := []string{"Apple", "banana", "apple", "Banana", "cherry"} slices.SortStableFunc(words, func(a, b string) int { aLower := strings.ToLower(a) bLower := strings.ToLower(b) if aLower < bLower { return -1 } if aLower > bLower { return 1 } return 0 }) fmt.Println("Case-insensitive sorted:", words) }
We convert strings to lowercase before comparison. The original order of "Apple" and "apple" is preserved due to stable sorting.
Sorting in Descending Order
To sort in descending order, we simply reverse the comparison logic. This example sorts numbers from highest to lowest.
package main import ( "fmt" "slices" ) func main() { numbers := []int{3, 1, 4, 1, 5, 9, 2, 6} slices.SortStableFunc(numbers, func(a, b int) int { if a > b { return -1 } if a < b { return 1 } return 0 }) fmt.Println("Descending sorted:", numbers) }
The comparison function returns -1 when a > b to achieve descending order. Equal elements maintain their original relative positions.
Sorting with Complex Logic
slices.SortStableFunc
can handle complex sorting criteria. This
example sorts strings by length, then alphabetically for equal lengths.
package main import ( "fmt" "slices" ) func main() { words := []string{"apple", "banana", "kiwi", "orange", "fig", "pear"} slices.SortStableFunc(words, func(a, b string) int { if len(a) < len(b) { return -1 } if len(a) > len(b) { return 1 } if a < b { return -1 } if a > b { return 1 } return 0 }) fmt.Println("Complex sorted:", words) }
The comparison function first compares lengths, then performs lexicographical comparison for strings of equal length. Stable sort preserves original order.
Practical Example: Employee Sorting
This practical example sorts employees by department, then by salary within departments, preserving original order for equal comparisons.
package main import ( "fmt" "slices" ) type Employee struct { Name string Department string Salary int } func main() { employees := []Employee{ {"Alice", "HR", 50000}, {"Bob", "IT", 75000}, {"Charlie", "HR", 50000}, {"David", "IT", 80000}, {"Eve", "Finance", 60000}, } slices.SortStableFunc(employees, func(a, b Employee) int { if a.Department < b.Department { return -1 } if a.Department > b.Department { return 1 } if a.Salary < b.Salary { return -1 } if a.Salary > b.Salary { return 1 } return 0 }) fmt.Println("Sorted employees:") for _, e := range employees { fmt.Printf("%s: %s $%d\n", e.Name, e.Department, e.Salary) } }
Employees are first sorted by department name, then by salary within each department. Original order is preserved for employees with identical fields.
Source
Go experimental slices package documentation
This tutorial covered the slices.SortStableFunc
function in Go with
practical examples of stable sorting with custom comparison functions.
Author
List all Go tutorials.