Standard library patterns · 3 MIN READ

Sorting by whatever you like

Sort a slice of structs by two keys.

slices.SortFunc takes a comparison that returns a negative number, zero or a positive one — not a bool. That trips people coming from languages where the comparator answers "is a before b". cmp.Compare does the right thing for any ordered type and is what the comparison should usually be built from.

For a second key, compare the first and fall through only when it is equal. That is the whole pattern, and it is worth writing out once.

SEE THE IDEA

Compare the first key, then break ties with the second

  1. 01

    Primary key

    go
    cmp.Compare(a.Year, b.Year)

    Negative, zero or positive

  2. 02

    Equal primary

    go
    if result == 0

    Only now compare another key

  3. 03

    Secondary key

    go
    cmp.Compare(a.Name, b.Name)

    Produces the final ordering

slices.SortFunc takes a three-way comparison, not a less-than boolean.

In practice

Make the comparison consistent and transitive so the sorting algorithm can rely on it. Sorting changes the supplied slice in place; clone first if callers need the original order. Use SortStableFunc when equal elements must retain their original relative order.

Watch out

Subtracting integers as a comparator can overflow. cmp.Compare expresses the ordering without that overflow risk.

Official reference ↗

Work through the exercise

go
package main

type Release struct {
	Name string
	Year int
}

// SortReleases orders by year ascending, and by name for
// releases from the same year. It sorts in place.
func SortReleases(releases []Release) {}

func main() {}
Try it yourself

Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.

Progress stays in this browser. No account needed.