Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Go  Module 4 – Arrays, slices, maps and strings

The slices package: sort, search and compare

Sort Go slices with slices.Sort and SortFunc, order rows by several keys with cmp.Compare and cmp.Or, and search sorted data with BinarySearch.

  • Intermediate
  • 15 minutes
  • Examples run with Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 6 of 15 minutes free
Free: slices.Sort puts numbers and strings in order, strings by their bytes · A comparison returns a sign, and cmp.Or chains the keys · BinarySearch finds a value, or the place where it belongs. With a pass: 2 in-depth parts and 3 interview answers, about 9 minutes.

What you will learn

  • Sort slices with slices.Sort and slices.SortFunc, and predict the byte order of strings
  • Write comparison functions on several keys with cmp.Compare and cmp.Or
  • Search a sorted slice with slices.BinarySearch and insert at the position it returns
  • Choose between slices.SortFunc, slices.SortStableFunc and the sort package
  • Remove, compact and compare elements with slices.Delete, slices.Compact and slices.Equal

Before you start

What you’ll learn · Before you start

What you will learn

  • Sort slices with slices.Sort and slices.SortFunc, and predict the byte order of strings
  • Write comparison functions on several keys with cmp.Compare and cmp.Or
  • Search a sorted slice with slices.BinarySearch and insert at the position it returns
  • Choose between slices.SortFunc, slices.SortStableFunc and the sort package
  • Remove, compact and compare elements with slices.Delete, slices.Compact and slices.Equal

Before you start

On this page

Six exam results ordered by marks, highest first, with tied marks in name order, take one call in Go: slices.SortFunc with a comparison built from cmp.Compare and cmp.Or. The slices package, part of the standard library since Go 1.21, also searches sorted data, edits slices in place and compares them, with generic functions that the compiler checks for each element type.

You will sort numbers and strings, write comparisons on several keys, find values with a binary search and insert new ones where they belong. Each output below is the one its program printed when it ran with Go 1.27.1.

slices.Sort puts numbers and strings in order, strings by their bytes

slices.Sort sorts any slice whose elements are ordered, integers, floats or strings, in place and from the smallest:

sort_basics.go: marks, names, and names without case Go · sort_basics.go
// sort_basics.go: slices.Sort on numbers and strings, and strings ordered by their bytes.
package main

import (
	"fmt"
	"slices"
	"strings"
)

func main() {
	marks := []int{67, 91, 42, 88, 58}
	slices.Sort(marks) // in place, smallest first
	fmt.Println(marks, slices.IsSorted(marks))

	names := []string{"arjun", "Zoya", "Meera", "bala"}
	slices.Sort(names) // byte by byte: 'M' (77) and 'Z' (90) come before 'a' (97)
	fmt.Println(names)

	slices.SortFunc(names, func(a, b string) int {
		return strings.Compare(strings.ToLower(a), strings.ToLower(b))
	})
	fmt.Println(names)
	fmt.Println(slices.Min(marks), slices.Max(marks), slices.Index(marks, 88))
}

Output

[42 58 67 88 91] true
[Meera Zoya arjun bala]
[arjun bala Meera Zoya]
42 91 3

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run sort_basics.go

The marks came out as expected, and slices.IsSorted confirmed it. The names did not: [Meera Zoya arjun bala]. Strings compare byte by byte, and in ASCII every capital letter, 65 to 90, comes before every small letter, 97 to 122, so both capitalised names sorted first. A comparison function: A function of two elements, a and b, that returns a negative number when a sorts first, a positive one when b does, and 0 when they tie, as slices.SortFunc expects. passed to slices.SortFunc can compare something else instead, here the names in lower case, which gave [arjun bala Meera Zoya]. The last line shows three more functions that read a slice: slices.Min, slices.Max and slices.Index, which returned 3, the position of 88 after the sort.

Lower-case keys once, not in every comparison

strings.ToLower inside a comparison builds two new strings on every call. For a large slice, compute each key once, store it next to its element, and sort by the stored key.

Checkpoint

Check: Sorting numbers and strings

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    w := []string{"banana", "Cherry", "apple"}
    slices.Sort(w)
    fmt.Println(w)
    Show the answer: Question 1 of 2

    Answer: [Cherry apple banana]

    Strings compare byte by byte, and every capital letter has a smaller byte than every small letter, as Meera and Zoya came before arjun in sort_basics.go.

  2. Question 2 of 2 What does slices.Sort(marks) return?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Nothing; it sorts marks in place

    slices.Sort reorders the slice it is given, which is why sort_basics.go could print marks right after the call.

A comparison returns a sign, and cmp.Or chains the keys

slices.SortFunc calls its function with two elements, a and b, and reads only the sign of the result: negative puts a first, positive puts b first, and zero means they tie. cmp.Compare(x, y) returns -1, 0 or +1, so it supplies the sign for one key, and cmp.Or returns the first of its arguments that is not zero, which chains keys:

leaderboard.go: highest marks first, then names in order Go · leaderboard.go
// leaderboard.go: marks highest first, then names A to Z where marks tie, with cmp.Compare and cmp.Or.
package main

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

type Result struct {
	Name  string
	Marks int
}

func main() {
	results := []Result{
		{"Ravi", 88}, {"Asha", 91}, {"Kiran", 88},
		{"Meera", 75}, {"Dev", 91}, {"Bala", 88},
	}
	slices.SortFunc(results, func(a, b Result) int {
		return cmp.Or(
			cmp.Compare(b.Marks, a.Marks), // b before a: highest marks first
			cmp.Compare(a.Name, b.Name),   // then names in order
		)
	})
	for i, r := range results {
		fmt.Printf("%d. %-5s %d\n", i+1, r.Name, r.Marks)
	}
	fmt.Println(cmp.Compare(88, 91), cmp.Compare(91, 91), cmp.Compare("Ravi", "Kiran"))
}

Output

1. Asha  91
2. Dev   91
3. Bala  88
4. Kiran 88
5. Ravi  88
6. Meera 75
-1 0 1

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run leaderboard.go

Swapping the arguments, cmp.Compare(b.Marks, a.Marks), turned the order round, so the marks ran from 91 down. The three rows with 88 tied on marks, and the names settled them. Follow the function through two of its calls:

Interactive · stage by stage How the leaderboard's comparison decides two pairs Step through two calls of the comparison and watch which key decides each pair.
How the leaderboard's comparison decides two pairs, step 6 of 6. Showing SortFunc asks about a (a = Asha 91, b = Ravi 88), Compare marks (88 with 91: -1), Compare names (computed, not used), What cmp.Or returns (-1: Asha before Ravi). In focus: What cmp.Or returns.not 0SortFunc asks about aa = Asha 91, b = Ravi 88Compare marks88 with 91: -1Compare namescomputed, not usedWhat cmp.Or returns-1: Asha before Ravi

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. SortFunc asks. While it sorts, SortFunc calls the function with two rows, a and b. A negative result puts a first, a positive one puts b first, and 0 means the two tie.
  2. Marks first. cmp.Compare(b.Marks, a.Marks) compares Kiran's 88 with Ravi's 88. They are equal, so it returns 0, and cmp.Or looks at its next argument.
  3. Then names. cmp.Compare(a.Name, b.Name) compares "Ravi" with "Kiran". R comes after K, so it returns 1, the first result that is not 0.
  4. The answer. cmp.Or returns 1, so SortFunc puts b, Kiran, before a, Ravi. The three rows with 88 end up in name order, Bala, Kiran, Ravi.
  5. A second pair. Now a is Asha with 91 and b is Ravi with 88. cmp.Compare(88, 91) returns -1, which is not 0, so cmp.Or returns it, and the result for the names goes unused.
  6. Asha first. -1 puts a, Asha, before Ravi. The marks decide every pair whose marks differ, and the names decide only the ties.

Both comparisons are evaluated before cmp.Or runs, because they are its arguments, so a key that is costly to compute belongs in an if that returns early instead. The package’s documentation adds one requirement, a strict weak ordering: the same pair must always get the same answer, and if a comes before b and b before c, a must come before c, or the sort may come out wrong.

Key idea

A comparison answers with a sign; cmp.Compare gives the sign for one key, and cmp.Or takes the first key that does not tie.

Checkpoint

Check: Comparisons on several keys

  1. Question 1 of 2 Which comparison sorts products from the highest price to the lowest?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: func(a, b Product) int { return cmp.Compare(b.Price, a.Price) }

    Swapping the arguments of cmp.Compare reverses the order, as cmp.Compare(b.Marks, a.Marks) did in leaderboard.go.

  2. Question 2 of 2 What does this print?

    Read the code, then choose one answer.

    fmt.Println(cmp.Or(0, -1, 1), cmp.Or(0, 0))
    Show the answer: Question 2 of 2

    Answer: -1 0

    In a comparison, each argument is the result for one key, so cmp.Or returns the first key that does not tie.

BinarySearch finds a value, or the place where it belongs

A binary search: A search of sorted data that looks at the middle element and drops the half that cannot hold the target, so n values need at most about log2 n looks. looks at the middle of a sorted slice and throws away the half that cannot hold the target, so 9 marks need at most 4 looks. Step through a search for 73 in these marks:

Interactive · step through it Binary search for 73 in nine sorted marks Each probe halves the range where the target can still be; the dimmed values are ruled out.
Array of 9: 35 42 58 61 67 73 88 91 95. lo at 0, hi at 8.350421582613674735886917958lohi
  • being compared or looked at
  • in its final place
  • visited or ruled out

Pseudo-code

  1. lo ← 0; hi ← n − 1
  2. while lo ≤ hi:
  3. mid ← lo + ⌊(hi − lo) / 2⌋
  4. if a[mid] = target: return mid
  5. if a[mid] < target: lo ← mid + 1
  6. else: hi ← mid − 1
  7. return "not found"

Look for 73. The invariant: if 73 is in the array, it is somewhere in a[lo..hi]. At the start that range is the whole array, positions 0 to 8.

When it finishes: Found 73 at index 5.

Probes 0
Positions left 9

The figure found 73 at index 5 with three probes. slices.BinarySearch halves the range in the same way, but it keeps going until it reaches the first position whose value is not below the target. That is why it can answer for a value that is missing as well:

binsearch.go: 73, which is there, and 70, which is not Go · binsearch.go
// binsearch.go: BinarySearch on sorted marks, for a value that is there and one that is not.
package main

import (
	"fmt"
	"slices"
)

func main() {
	marks := []int{35, 42, 58, 61, 67, 73, 88, 91, 95}

	i, found := slices.BinarySearch(marks, 73)
	fmt.Println(i, found) // where 73 is

	i, found = slices.BinarySearch(marks, 70)
	fmt.Println(i, found) // where 70 would go

	marks = slices.Insert(marks, i, 70) // inserting there keeps the slice sorted
	fmt.Println(marks, slices.IsSorted(marks))
}

Output

5 true
5 false
[35 42 58 61 67 70 73 88 91 95] true

Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run binsearch.go

For 73 it returned 5 true; for 70, 5 false: 70 is not there, and index 5 is where it would go. slices.Insert at that index kept the slice sorted. The search trusts its input, though: on a slice that is not sorted it returns wrong answers without any error.

Checkpoint

Check: Binary search and insertion

  1. Question 1 of 2 What does this print?

    Read the code, then choose one answer.

    i, ok := slices.BinarySearch([]int{10, 20, 30, 40}, 25)
    fmt.Println(i, ok)
    Show the answer: Question 1 of 2

    Answer: 2 false

    For 70 in the marks, binsearch.go printed 5 false: the place where 70 belongs, and that it was not found.

  2. Question 2 of 2 A sorted list of blocked ids gets new ids appended at its end without sorting. What does slices.BinarySearch do for a blocked id that was appended?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It may report it as not found, without any error

    The slice must be sorted in increasing order, the documentation says; keep it sorted by inserting at the position BinarySearch returns, or sort again after a batch of appends.

The package has more: slices.SortStableFunc is a stable sort: A sort that keeps equal elements in the order they had before it ran, such as slices.SortStableFunc. slices.SortFunc does not promise to., which keeps equal elements in the order they had, as slices.SortFunc does not promise to. slices.Delete removes a range of elements in place. slices.Compact removes repeated values that sit next to each other. slices.Replace puts new elements in place of a range. slices.Equal reports whether two slices hold the same elements in the same order. The older sort package sorts through an interface or a function that compares two indexes.

Stable sorts, comparisons that break, and the sort package, measured Pro: opens with a Pro, Premium or Ultimate pass

A sort can be correct and still surprise: ties that move between runs of a program, a comparison that overflows, a NaN that sorts first. This part reproduces each one on Go 1.27.1 and measures what the older sort package costs.

In this part:

  • Ties reproduced: SortFunc moves equal elements, SortStableFunc keeps them
  • A comparison that subtracts can overflow
  • NaN sorts first, and Max returns NaN
  • The sort package: an interface, a less function, and two allocations

5 runnable examples (Go) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Delete, Compact, Insert and Equal: what each does to the backing array Pro: opens with a Pro, Premium or Ultimate pass

The editing functions of the slices package change the array they are given and return the slice to use from then on. This part shows what each one leaves behind for a caller who keeps the old slice, and builds a TopK that never touches its input.

In this part:

  • slices.Delete zeroes the tail that the old slice can still see
  • Compact removes runs; sort first to remove every repeat
  • Insert, Replace and Equal
  • TopK without touching the input

4 runnable examples (Go) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise

Practice covers sorting by bytes, comparisons on several keys and binary search, starting with the idea your earlier answers left weakest. Each choice explains why it is right or what it misses:

Practice that adapts to you

Practise this lesson

8 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 8 Which slices can slices.Sort sort without a comparison function?

    Choose every answer that is right.

    Show the answer: Question 1 of 8

    Answer:

    • []int
    • []float64
    • []string

    slices.Sort takes elements of an ordered type, integers, floats and strings; anything else needs SortFunc.

  2. Question 2 of 8 What does this print?

    Read the code, then choose one answer.

    n := []string{"bob", "Alice", "carol", "Dave"}
    slices.Sort(n)
    fmt.Println(n)
    Show the answer: Question 2 of 8

    Answer: [Alice Dave bob carol]

    Byte order sorts every capitalised string before every small-letter one, as sort_basics.go showed.

  3. Question 3 of 8 A comparison function returns -3 for the pair a, b. What does SortFunc do with that?

    Choose one answer.

    Show the answer: Question 3 of 8

    Answer: It puts a before b; only the sign of the result counts

    SortFunc reads a negative result as a before b and a positive one as b before a.

  4. Question 4 of 8 In cmp.Or(cmp.Compare(a.Day, b.Day), slowKey(a, b)), when is slowKey called?

    Choose one answer.

    Show the answer: Question 4 of 8

    Answer: On every comparison, because arguments are evaluated before cmp.Or runs

    For a costly key, write the comparison with an if that returns as soon as the cheap key decides.

  5. Question 5 of 8 Products must be ordered by category A to Z, then by price from lowest, then by name. Which comparison does it?

    Choose one answer.

    Show the answer: Question 5 of 8

    Answer: cmp.Or(cmp.Compare(a.Category, b.Category), cmp.Compare(a.Price, b.Price), cmp.Compare(a.Name, b.Name))

    List the keys in order of importance inside cmp.Or, and choose the argument order of each Compare for its direction.

  6. Question 6 of 8 What does this print?

    Read the code, then choose one answer.

    j, ok := slices.BinarySearch([]int{2, 4, 4, 4, 8}, 4)
    fmt.Println(j, ok)
    Show the answer: Question 6 of 8

    Answer: 1 true

    The search narrows the range until it reaches the first position whose value is not below the target.

  7. Question 7 of 8 At most how many middle elements does a binary search look at in 1,000 sorted values?

    Type a number.

    A hint

    Each look halves what is left.

    Show the answer: Question 7 of 8

    Answer: 10 elements

    Each look halves the range that is left, and ⌊log₂ 1000⌋ + 1 = 9 + 1 = 10, so 10 looks at most; 9 marks needed at most 4.

  8. Question 8 of 8 How do you add 70 to sorted marks so that they stay sorted?

    Choose one answer.

    Show the answer: Question 8 of 8

    Answer: i, _ := slices.BinarySearch(marks, 70); marks = slices.Insert(marks, i, 70)

    binsearch.go inserted 70 at index 5, the position BinarySearch returned, and IsSorted stayed true.

Interview questions

Warm-up (fresher): Why does slices.Sort put “Zoya” before “arjun”?

Strings compare byte by byte, and the byte of a capital letter is smaller than that of any small letter: “Z” is 90 and “a” is 97. So every name that starts with a capital comes first. For an order that ignores case, sort with slices.SortFunc and compare lower-case forms of the names.

Three longer questions of the module bank build on this lesson:

  1. Fresher to mid

    A code review finds slices.SortFunc(accounts, func(a, b Account) int { return int(a.Balance - b.Balance) }) with int64 balances, where math.MinInt64 marks an account with no balance yet. The tests pass. What can go wrong, and what do you write instead?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid

    A transactions page sorts its rows by date with slices.SortFunc. Users report that rows with the same date change places when they refresh, but the tests, which use ten rows, always pass. Explain both observations and fix it.

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A service checks each request against one million blocked ids kept in a sorted slice with slices.BinarySearch. A new feature appends ids to the end of the slice as they are reported, and some blocked ids now get through. Why, and how do you keep the check correct and fast?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • slices.Sort sorts any ordered type in place, and strings sort by their bytes, so capitals come before small letters.
  • A comparison returns a sign; cmp.Compare gives it for one key, swapping its arguments reverses the order, and cmp.Or chains keys by returning the first result that is not zero.
  • slices.BinarySearch returns the position of the target, or where it belongs, and whether it was found; the slice must already be sorted.

Terms in this lesson

binary search
A search of sorted data that looks at the middle element and drops the half that cannot hold the target, so n values need at most about log2 n looks.
comparison function
A function of two elements, a and b, that returns a negative number when a sorts first, a positive one when b does, and 0 when they tie, as slices.SortFunc expects.
stable sort
A sort that keeps equal elements in the order they had before it ran, such as slices.SortStableFunc. slices.SortFunc does not promise to.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.