๐ŸŽ“ A1Learner
๐Ÿ”
0 of 34 lessons completed

Go Sorting in Depth

Sorting places the values of a slice into a chosen order. Go sorts in place, so the slice itself holds the new order afterward.

Sorting by hand with sort.Interface

ByLength is a string slice type with the three methods. Less compares the lengths of two words, so the shortest word lands first.

package main

import (
	"fmt"
	"sort"
)

type ByLength []string

func (b ByLength) Len() int {
	return len(b)
}

func (b ByLength) Less(i, j int) bool {
	return len(b[i]) < len(b[j])
}

func (b ByLength) Swap(i, j int) {
	b[i], b[j] = b[j], b[i]
}

func main() {
	words := ByLength{"banana", "fig", "kiwi"}
	sort.Sort(words)
	fmt.Println(words)
}

SortFunc sorts in falling order

cmp.Compare(a, b) would sort in rising order. Passing the inputs swapped turns the result around, so the biggest number lands first.

package main

import (
	"cmp"
	"fmt"
	"slices"
)

func main() {
	nums := []int{2, 8, 5}
	slices.SortFunc(nums, func(a, b int) int {
		return cmp.Compare(b, a)
	})
	fmt.Println(nums)
}

BinarySearch on a rising slice

The slice is already in rising order, as the search needs. The search reports the index and true for a hit, and an index with false for a miss.

package main

import (
	"fmt"
	"slices"
)

func main() {
	nums := []int{2, 5, 8}
	i, found := slices.BinarySearch(nums, 5)
	fmt.Println(i, found)
	j, ok := slices.BinarySearch(nums, 6)
	fmt.Println(j, ok)
}

A stable sort keeps equal scores in order

Ana and Bo both hold a score of 90. The stable sort orders by score, and Ana stays ahead of Bo, as in the starting slice.

package main

import (
	"cmp"
	"fmt"
	"slices"
)

type Result struct {
	Name  string
	Score int
}

func main() {
	results := []Result{{"Ana", 90}, {"Bo", 90}, {"Cy", 80}}
	slices.SortStableFunc(results, func(a, b Result) int {
		return cmp.Compare(b.Score, a.Score)
	})
	fmt.Println(results)
}

Example

package main

import (
	"cmp"
	"fmt"
	"slices"
)

type Entry struct {
	Name  string
	Score int
}

func main() {
	board := []Entry{{"Ana", 70}, {"Bo", 95}, {"Cy", 80}}
	slices.SortFunc(board, func(a, b Entry) int {
		return cmp.Compare(a.Score, b.Score)
	})
	fmt.Println(board)
	scores := []int{}
	for _, e := range board {
		scores = append(scores, e.Score)
	}
	i, found := slices.BinarySearch(scores, 80)
	fmt.Println(i, found)
	fmt.Println(board[i].Name)
}

Key points

  • sort.Interface asks a type for three methods named Len, Less and Swap.
  • Less reports whether the value at index i must come before the value at index j.
  • sort.Sort reads the three methods and orders the values in place.
  • slices.SortFunc orders a slice with a comparison function given as an argument.
  • The comparison function returns a negative number, zero or a positive number.
  • slices.BinarySearch finds a value only in a slice sorted in rising order.
  • A stable sort keeps equal values in the order they started in.

In this example, SortFunc orders the board by score in rising order. A second slice collects the scores in that same order. BinarySearch looks for 80 in the sorted scores and finds it at index 1. The last line prints the name at that index on the board, and the name is Cy.

Note

sort.Sort makes no promise about equal values, so a stable sort is the safe pick when their order matters.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Go Playground

Exercise

What must be true of a slice before slices.BinarySearch can search it?