Sorting is a very common use case that we encounter in day-to-day programming. In this article, you’ll learn how to sort a slice of primitive types (string, int, float64) and user-defined collections in Go.
Sorting a Slice of Strings, Integers, or Floats in Go
The sort package in Go provides several convenience methods for sorting a slice of primitive types. The following example demonstrates how to sort a slice of strings, integers, and floats in Go:
package main
import (
"fmt"
"sort"
)
func main() {
// Sorting a slice of Strings
strs := []string{"quick", "brown", "fox", "jumps"}
sort.Strings(strs)
fmt.Println("Sorted strings: ", strs)
// Sorting a slice of Integers
ints := []int{56, 19, 78, 67, 14, 25}
sort.Ints(ints)
fmt.Println("Sorted integers: ", ints)
// Sorting a slice of Floats
floats := []float64{176.8, 19.5, 20.8, 57.4}
sort.Float64s(floats)
fmt.Println("Sorted floats: ", floats)
}
$ go run sorting.go
Sorted strings: [brown fox jumps quick]
Sorted integers: [14 19 25 56 67 78]
Sorted floats: [19.5 20.8 57.4 176.8]
Sorting a Slice in Reverse order
To sort a slice in reverse order, you can use the Reverse()
function along with StringSlice
, IntSlice
, and Float64Slice
types. These types implement the Interface
for comparision and swap defined in the sort
package.
The Reverse()
function returns the reverse order of data. Let’s look at an example of sorting a slice of strings, integers, and floats in reverse order:
package main
import (
"fmt"
"sort"
)
func main() {
// Sorting a slice of Strings
strs := []string{"quick", "brown", "fox", "jumps"}
sort.Sort(sort.Reverse(sort.StringSlice(strs)))
fmt.Println("Sorted strings in reverse order: ", strs)
// Sorting a slice of Integers
ints := []int{56, 19, 78, 67, 14, 25}
sort.Sort(sort.Reverse(sort.IntSlice(ints)))
fmt.Println("Sorted integers in reverse order: ", ints)
// Sorting a slice of Floats
floats := []float64{176.8, 19.5, 20.8, 57.4}
sort.Sort(sort.Reverse(sort.Float64Slice(floats)))
fmt.Println("Sorted floats in reverse order: ", floats)
}
$ go run reverse-sorting.go
Sorted strings in reverse order: [quick jumps fox brown]
Sorted integers in reverse order: [78 67 56 25 19 14]
Sorted floats in reverse order: [176.8 57.4 20.8 19.5]
Sorting a slice using a comparator function in Go
Many times, you would want to sort a collection by something other than its natural order. For example, you would want to sort a slice of strings by their length, or you would want to sort a slice of users by their age.
For these usecases, you can use the Slice()
and SliceStable()
functions provided by the sort
package. These are higher order functions that accept a less
function as an argument:
func Slice(x interface{}, less func(i, j int) bool)
func SliceStable(x interface{}, less func(i, j int) bool)
The less
function is a comparator function that reports whether the element at index i
should sort before the element at index j
. If both less(i, j)
and less(j, i)
are false, then the elements at index i
and j
are considered equal.
When you use the Slice()
function, the sort is not guaranteed to be stable: equal elements may be reversed from their original order. For a stable sort, use SliceStable()
.
Let’s look at an example:
package main
import (
"fmt"
"sort"
)
func main() {
// Sorting a slice of strings by length
strs := []string{"United States", "India", "France", "United Kingdom", "Spain"}
sort.Slice(strs, func(i, j int) bool {
return len(strs[i]) < len(strs[j])
})
fmt.Println("Sorted strings by length: ", strs)
// Stable sort
sort.SliceStable(strs, func(i, j int) bool {
return len(strs[i]) < len(strs[j])
})
fmt.Println("[Stable] Sorted strings by length: ", strs)
// Sorting a slice of strings in the reverse order of length
sort.SliceStable(strs, func(i, j int) bool {
return len(strs[j]) < len(strs[i])
})
fmt.Println("[Stable] Sorted strings by reverse order of length: ", strs)
}
$ go run sorting-by-comparator.go
Sorted strings by length: [India Spain France United States United Kingdom]
[Stable] Sorted strings by length: [India Spain France United States United Kingdom]
[Stable] Sorted strings by reverse order of length: [United Kingdom United States France India Spain]
Sorting a slice of structs using a comparator function
This is a continuation of the previous example. This example demonstrates how to sort a collection of user-defined structs by passing a custom less
function to the Slice()
and SliceStable()
functions:
package main
import (
"fmt"
"sort"
)
type User struct {
Name string
Age int
}
func main() {
// Sorting a slice of structs by a field
users := []User{
{
Name: "Rajeev",
Age: 28,
},
{
Name: "Monica",
Age: 31,
},
{
Name: "John",
Age: 56,
},
{
Name: "Amanda",
Age: 16,
},
{
Name: "Steven",
Age: 28,
},
}
// Sort users by their age
sort.Slice(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})
fmt.Println("Sorted users by age: ", users)
// Stable sort
sort.SliceStable(users, func(i, j int) bool {
return users[i].Age < users[j].Age
})
fmt.Println("Sorted users by age: ", users)
}
Custom sorting by implementing sort.Interface
Finally, let’s look at the most generic way of sorting a collection of primitives or user-defined structs in Go. To enable custom sorting of a collection of any type, you need to define a corresponding type that implements the generic Interface
provided by the sort
package. The Interface
contains the following methods:
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with index i must sort before the element with index j.
// If both Less(i, j) and Less(j, i) are false, then the elements at index i and j are considered equal.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
After implementing the above Interface
, you can use the Sort()
or Stable()
functions that sort any collection that implements the sort.Interface
interface.
Let’s understand with an example:
package main
import (
"fmt"
"sort"
)
type User struct {
Name string
Age int
}
// Define a collection type that implements sort.Interface
type UsersByAge []User
func (u UsersByAge) Len() int {
return len(u)
}
func (u UsersByAge) Swap(i, j int) {
u[i], u[j] = u[j], u[i]
}
func (u UsersByAge) Less(i, j int) bool {
return u[i].Age < u[j].Age
}
func main() {
users := []User{
{
Name: "Rajeev",
Age: 28,
},
{
Name: "Monica",
Age: 31,
},
{
Name: "John",
Age: 56,
},
{
Name: "Amanda",
Age: 16,
},
{
Name: "Steven",
Age: 28,
},
}
// Sorting a slice of users by age (Sort may place equal elements in any order in the final result)
sort.Sort(UsersByAge(users))
fmt.Println("Sorted users by age: ", users)
// Stable Sorting (Stavle sort preserves the original input order of equal elements)
sort.Stable(UsersByAge(users))
fmt.Println("[Stable] Sorted users by age: ", users)
}
$ go run custom-sorting-struct.go
Sorted users by age: [{Amanda 16} {Rajeev 28} {Steven 28} {Monica 31} {John 56}]
[Stable] Sorted users by age: [{Amanda 16} {Rajeev 28} {Steven 28} {Monica 31} {John 56}]