How to Sort a Slice in Go
Sorting is a common task in programming, and for that reason, most languages have a default sorting algorithm in their standard library. Go is one such language. Go has gone about providing sorting functionality in one of the most elegant ways possible, via an interface.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
Any type that satisfies this interface can be sorted using the standard library’s sort.Sort() function. There is rarely a reason to sort any other way because the sort function is O(n log(n)) in the worst case. You can take a look at the various algorithms that are used, depending on the data to be sorted, here.
Sorting a Slice
The first thing to do no matter what we are sorting is to create a custom type. A custom type will allow us to implement the Len(), Less() and Swap() methods on it.
type mySlice []int
Then we implement the methods to fulfill the sort.Interface interface:
func (ms mySlice) Len() int {
return len(ms)
}
func (ms mySlice) Less(i, j int) bool {
return ms[i] < ms[j]
}
func (ms mySlice) Swap(i, j int) {
ms[i], ms[j] = ms[j], ms[i]
}
Let’s test it out:
package main
import (
"fmt"
"math/rand"
"sort"
)
type mySlice []int
func (ms mySlice) Len() int {
return len(ms)
}
func (ms mySlice) Less(i, j int) bool {
return ms[i] < ms[j]
}
func (ms mySlice) Swap(i, j int) {
ms[i], ms[j] = ms[j], ms[i]
}
func main() {
ms := mySlice{}
for i := 0; i < 10; i++ {
ms = append(ms, rand.Intn(100))
}
fmt.Println("pre-sort:", ms)
sort.Sort(ms)
fmt.Println("post-sort:", ms)
}
prints:
pre-sort: [81 87 47 59 81 18 25 40 56 0]
post-sort: [0 18 25 40 47 56 59 81 81 87]
Changing It Up
If we want different behavior we just change the way we implement the interface. For example, if we want to sort greatest to least we just change the Less() function:
func (ms mySlice) Less(i, j int) bool {
return ms[i] > ms[j]
}
which prints:
pre-sort: [81 87 47 59 81 18 25 40 56 0]
post-sort: [87 81 81 59 56 47 40 25 18 0]
Other Types
Sorting integers is pretty boring. Besides, if we are just going to sort integers we can use the pre-defined IntSlice type instead of coding it all again ourselves. Let’s sort a slice of structs:
type mtgCard struct {
manaCost int
name string
}
type mtgCards []mtgCard
Now we implement the sorting:
func (mCards mtgCards) Len() int {
return len(mCards)
}
func (mCards mtgCards) Less(i, j int) bool {
if mCards[i].manaCost < mCards[j].manaCost {
return true
} else if mCards[i].manaCost == mCards[j].manaCost {
return mCards[i].name < mCards[j].name
}
return false
}
func (mCards mtgCards) Swap(i, j int) {
mCards[i], mCards[j] = mCards[j], mCards[i]
}
The Less() function we made will sort by mana cost unless there is a tie, in which case it will sort by name. Let’s test it out:
func main() {
mCards := mtgCards{
{
manaCost: 7,
name: "ajani",
},
{
manaCost: 2,
name: "liliana",
},
{
manaCost: 2,
name: "chandra",
},
{
manaCost: 4,
name: "garruk",
},
{
manaCost: 4,
name: "jace",
},
{
manaCost: 5,
name: "bolas",
},
}
fmt.Println("pre-sort:", mCards)
sort.Sort(mCards)
fmt.Println("post-sort:", mCards)
}
Prints:
pre-sort: [{7 ajani} {2 liliana} {2 chandra} {4 garruk} {4 jace} {5 bolas}]
post-sort: [{2 chandra} {2 liliana} {4 garruk} {4 jace} {5 bolas} {7 ajani}]
Don’t Reinvent It
The standard library’s sorting methods are powerful, don’t code them yourself unless you have a very extreme use case. Take a look at some of the other functionality provided by the sort package as well if you have time. Searching, stable sorting, and checking if a type is sorted are some pretty awesome features.
Related Articles
Don't Go To Casting Hell - Use Default Native Types in Go
May 21, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
Go is strongly typed, and with that, we get many options for simple variable types like integers and floats. The problem arises when we have a uint16, and the function we are trying to pass it into takes an int. We find code riddled with int(myUint16) that can become slow and annoying to read. In other words, when Go developers stray from the “default” type for any given type family, the code can get messy quickly.
Rust vs Go - Which Is More Popular?
May 06, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
Go and Rust are two of the hottest compiled programming languages, but which is more popular, Go or Rust?. I develop in Go full-time and love it, and I’m learning more about Rust recently - it’s an exciting language. Let’s explore some differences between the two and look at which is growing faster in the popularity polls.
Range Over Ticker In Go With Immediate First Tick
Apr 30, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
The Go standard library has a really cool type - Ticker. Tickers are used when you want to do something at a regular interval, similar to JavaScript’s setInterval. Here’s an example:
Using 'Go Generate' To Deploy Multi-Process Apps
Apr 22, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
In microservice architectures, it’s fairly common to have a project that includes different worker types. A Makefile can be used to manage the creation of multiple programs, but the Go toolchain has a tool that can be used as well, go generate. Here are some examples of how it can be used: