HOWTO · Go
How to Sort Slice of Structs in Go
This tutorial demonstrates how to sort the slice of structs in GoLang.
On this page
Use the standard-library sort package to order a slice of structs. sort.Slice accepts the slice and a comparison function that receives two indices; compare the fields of slice[i] and slice[j]. It sorts the slice in place, and reversing < to > changes ascending order to descending order.
The examples show the resulting slice after each sort. Because sorting is in place, callers that still need the input order should copy the slice first; copying only the slice header does not preserve an independent ordering.
Sort a Slice of Structs by One Field with sort.Slice
This complete example orders employees by salary from lowest to highest. The comparison function reads each employee through its index in the original slice.
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
func main() {
employees := []Employee{
{Name: "John", Salary: 1500},
{Name: "Joe", Salary: 3000},
{Name: "Jack", Salary: 3400},
}
sort.Slice(employees, func(i, j int) bool {
return employees[i].Salary < employees[j].Salary
})
fmt.Println(employees)
}
[{John 1500} {Joe 3000} {Jack 3400}]
The < comparison places the lower salary first. Use > in the comparison to sort from highest salary to lowest. sort.Slice changes the supplied slice and does not promise to preserve the order of records with equal salaries.
Preserve Equal-Key Order with sort.SliceStable
sort.SliceStable also sorts structs using an index-based comparison function. Its stability guarantee means records with equal keys keep their relative order from the input. This is useful when that order carries meaning, such as an earlier ranking or import sequence.
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
func main() {
employees := []Employee{
{Name: "Zoe", Salary: 3000},
{Name: "John", Salary: 1500},
{Name: "Ava", Salary: 3000},
}
sort.SliceStable(employees, func(i, j int) bool {
return employees[i].Salary < employees[j].Salary
})
fmt.Println(employees)
}
[{John 1500} {Zoe 3000} {Ava 3000}]
The salary comparison orders records by salary while retaining Zoe before Ava because both salaries are equal and Zoe appeared first. Stable sorting works with structs just as it does with other slice element types.
Sort by Multiple Fields with a Tie-Breaker
To sort by a primary field and then a secondary field, compare the primary values first. When they match, compare the secondary values. This example orders positions alphabetically, then names alphabetically within each position.
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Position string
}
func main() {
employees := []Employee{
{Name: "Michael", Position: "Developer"},
{Name: "Jack", Position: "Manager"},
{Name: "Joe", Position: "CEO"},
{Name: "Leonard", Position: "Intern"},
{Name: "Sheldon", Position: "Developer"},
}
sort.Slice(employees, func(i, j int) bool {
if employees[i].Position != employees[j].Position {
return employees[i].Position < employees[j].Position
}
return employees[i].Name < employees[j].Name
})
fmt.Println(employees)
}
[{Joe CEO} {Michael Developer} {Sheldon Developer} {Leonard Intern} {Jack Manager}]
The comparator must be consistent: it orders by Position, and consults Name only when the positions match. The second comparison is the tie-breaker; the ordering follows both ascending comparisons. sort.SliceStable is not needed here because the comparator itself defines the order within each position.
Implement sort.Interface for Reusable Sorting
For a reusable sorting rule, define a named slice type and implement sort.Interface. Len reports the number of items, Less defines their order, and Swap exchanges two items. Then pass the slice to sort.Sort.
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
type BySalary []Employee
func (employees BySalary) Len() int { return len(employees) }
func (employees BySalary) Swap(i, j int) { employees[i], employees[j] = employees[j], employees[i] }
func (employees BySalary) Less(i, j int) bool { return employees[i].Salary < employees[j].Salary }
func main() {
employees := BySalary{
{Name: "John", Salary: 1500},
{Name: "Joe", Salary: 3000},
{Name: "Jack", Salary: 3400},
}
sort.Sort(employees)
fmt.Println(employees)
}
[{John 1500} {Joe 3000} {Jack 3400}]
Here BySalary implements the three required methods, so sort.Sort can arrange it by salary. This pattern is useful when the same ordering rule should be named and reused, or when sorting logic belongs with a custom collection type. For a one-off comparison, sort.Slice is usually shorter.
Choose sort.Slice when the ordering is local to one operation or depends on a caller’s current choice of field. Use sort.Interface when a named ordering is reused in several places or belongs to a collection abstraction. In either case, decide whether equal-key records may move relative to one another; select sort.SliceStable when their original sequence must remain visible.