In Go, sort behaviour is specific to the type of data being sorted. For example a slice of strings is sorted alphabetically, and ints numerically:


import "sort"
import "fmt"

countries := []string{"canada", "australia", "bahamas"}
sort.Strings(countries)

ages := []int{45, 27, 33}
sort.Ints(ages)

fmt.Println("countries":, countries)
// countries: [australia bahamas canada]

fmt.Println("ages":, ages)
// ages: [27 33 45]

I like to think of it this way: the type defines the sort. This isn’t javascript, you can’t just take a bunch of objects and sort them however you like by running them through a callback. The type defines the sort, so if you want to sort in some custom way, then you’ve got to define a custom type with that sorting behaviour baked in.

In practice, baking sorting behaviour into a type means defining three methods: Len, Less and Swap.


type Person struct {
  Name      string
  Age       int
}

// how do we want a slice of Person structs to sort?
type People []Person

func (slice People) Len() int {
	return len(slice)
}

func (slice People) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

// where we actually define sort behaviour
func (slice People) Less(i, j int) bool {
	return slice[i].Name < slice[j].Name;
}

And then in practice:


people := []People{
  {Name: "Sam", Age: 24},
  {Name: "Albert", Age: 63},
  {Name: "Ben", Age: 36},
}

// we defined the People type such that it will
// sort alphabetically by Name
sort.Sort(people)
fmt.Println(people)
// [{Albert 63} {Ben 36} {Sam 24}]


well this seems inflexible

It is and it isn’t. A type can only sort in the way that the type is defined to. So if you want to change how it sorts… you just need to change the type.


type Person struct {
  Name      string
  Age       int
}

// still a slice of people, but with different sorting behaviour
type ByAge []Person

func (slice ByAge) Len() int {
	return len(slice)
}

func (slice ByAge) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}

func (slice ByAge) Less(i, j int) bool {
  return slice[i].Age < slice[j].Age;
}

people := []People{
  {Name: "Sam", Age: 24},
  {Name: "Albert", Age: 63},
  {Name: "Ben", Age: 36},
}

// Cast to ByAge - which is still a slice of Person structs,
// but with a different sorting interface
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Sam 24} {Ben 36} {Albert 63}]