Start learning free

LESSON 199 OF 338 · SORTING

Sorting in Go: sort.Ints and sort.Slice

Lesson Read the full explanation

In order to sort a slice we can use the sort package from the standard library, which provides functions to sort slices in ascending order (default), descending order or user-defined order.

Syntax:

The main sorting functions in the sort package are:

sort.Ints(slice)
sort.Strings(slice)
sort.Slice(slice, func(i, j int) bool { ... })

Let's see some examples below. Sorting an array of integers in ascending order

numbers := []int{11, 3, 7, 5, 2}

sort.Ints(numbers) // numbers becomes [2, 3, 5, 7, 11]

Sorting an array of integers in descending order

numbers := []int{11, 3, 7, 5, 2}

sort.Sort(sort.Reverse(sort.IntSlice(numbers)))

Sorting an array of Strings in descending order

names := []string{"Mircea", "Andy", "Xavier", "Michael", "Christina"}

sort.Sort(sort.Reverse(sort.StringSlice(names)))  // --> ['Xavier', 'Mircea', 'Michael', 'Christina', 'Andy']

Sort with custom function (comparator):

sort.Slice() supports a less function that we can use to specify how we want to sort:

func less(i, j int) bool {
    return numbers[i] < numbers[j]
}

This can be written as an anonymous function (nameless function we can easily pass to sort.Slice()) as follows:

func(i, j int) bool {
    return numbers[i] < numbers[j]
}

In Go, the less function receives indices i and j and accesses the slice elements to compare them:

func(i, j int) bool {
    return slice[i] < slice[j]
}

When the sort.Slice() function compares two elements, it calls the less function with their indices, and sorts the elements according to the returned value:

  • If the result is true then the element at index i is sorted before the element at index j.
  • If the result is false then the element at index j is sorted before the element at index i.

All we need to do is pass the anonymous function inside sort.Slice():

numbers := []int{3, 23, 12}

sort.Slice(numbers, func(i, j int) bool { return numbers[i] < numbers[j] }) // --> 3, 12, 23

To demonstrate a basic example, let's assume the requirement for ordering a specific list is the length of the strings in the list, shortest to longest. We will be using the len() function of each string to compare them.

words := []string{"banana", "pie", "Washington", "book"}

sort.Slice(words, func(i, j int) bool { return len(words[i]) < len(words[j]) }) // --> ['pie', 'book', 'banana', 'Washington']

Example: sorting array of pairs

Let's say we want to sort an array, where each element is another array consisting of two numbers.

pairs := [][]int{{1, 5}, {12, 5}, {10, 1}, {1, 3}}

sort.Slice(pairs, func(i, j int) bool {
    // in case the first values of each pair are equal,
    // compare them using their second values
    if pairs[i][0] == pairs[j][0] {
        return pairs[i][1] < pairs[j][1]
    }
    return pairs[i][0] < pairs[j][0]
})
// pairs --> [[1, 3], [1, 5], [10, 1], [12, 5]]

Time Complexity:

Sorting a slice via sort.Slice() takes O(n * log n * C) time, where C is the time taken to compare two elements from the array.

It takes O(1) (constant time) to compare two numbers, therefore sorting an array of numbers takes O(n log n).

However, it takes O(L) to compare two strings, therefore sorting an array of strings takes O(n * log n * L), where L is the average length of the strings.


Space Complexity:

The sort.Slice() function sorts the elements of a slice in place (transforms the slice directly, using no auxiliary data structure), so the extra space used for this is O(1).


Assignment
Follow the Coding Tutorial and let's sort some arrays.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 6
    Ctrl + Enter to check

    GO · SORTING

    Sorting in Go, explained

    The sort package

    Go's standard library sorts slices with the sort package. The three functions you will use most are:

    sort.Ints(slice)
    sort.Strings(slice)
    sort.Slice(slice, func(i, j int) bool { ... })

    All of them sort in place: they rearrange the elements of the slice you pass and return nothing. If the original order is needed afterwards, copy the slice first.

    Ascending and descending order

    sort.Ints and sort.Strings sort in ascending order:

    numbers := []int{11, 3, 7, 5, 2}
    sort.Ints(numbers)
    fmt.Println(numbers)
    [2 3 5 7 11]

    For descending order, wrap the slice in a type from the package and reverse it:

    sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
    fmt.Println(numbers)
    [11 7 5 3 2]

    Strings work the same way with sort.StringSlice. Strings compare by bytes, so every uppercase letter sorts before every lowercase one: sort.Strings on "Mircea", "john", "Andy" gives [Andy Mircea john].

    Custom order with sort.Slice

    sort.Slice takes a less function. It receives two indices, i and j, and returns true when the element at i must come before the element at j. It receives positions rather than values, so the function reads the elements from the slice itself:

    numbers := []int{3, 23, 12}
    sort.Slice(numbers, func(i, j int) bool { return numbers[i] < numbers[j] })

    This gives [3 12 23]. The function is written inline, as an anonymous function, and it can use the variable numbers because closures capture variables from the surrounding code.

    Sorting words by length, shortest first:

    words := []string{"banana", "pie", "Washington", "book"}
    sort.Slice(words, func(i, j int) bool { return len(words[i]) < len(words[j]) })

    The result is [pie book banana Washington].

    Sorting by two keys

    When the first values are equal, break the tie with the second. Compare the first values for equality, then return the comparison of the second values; otherwise return the comparison of the first:

    pairs := [][]int{{1, 5}, {12, 5}, {10, 1}, {1, 3}}
    
    sort.Slice(pairs, func(i, j int) bool {
        if pairs[i][0] == pairs[j][0] {
            return pairs[i][1] < pairs[j][1]
        }
        return pairs[i][0] < pairs[j][0]
    })

    The result is [[1 3] [1 5] [10 1] [12 5]].

    Gotchas

    • The function gets indices. Writing return i < j compares positions, not values, and leaves the slice as it was.
    • Use a strict comparison. A less function must return false for equal elements. Writing <= gives an inconsistent ordering and can produce wrong results.
    • sort.Slice is not stable. Elements that compare equal may end up in any relative order. Use sort.SliceStable when ties must keep their original order.
    • It sorts a slice, not an array value. Pass a slice; for a fixed-size array write arr[:].
    • It returns nothing. numbers = sort.Ints(numbers) does not compile.

    Checking and reusing a sort

    To test whether a slice is already sorted, sort.IntsAreSorted(numbers) returns a boolean. After the descending sort above it returns false, because the function checks for ascending order. To sort by length from longest to shortest while keeping the original order of equal lengths, flip the comparison and use the stable variant: sort.SliceStable(words, func(i, j int) bool { return len(words[i]) > len(words[j]) }) gives [Washington banana book pie].

    The same pattern works for any element type. For a slice of structs, the less function compares the fields you care about, and a tie on the first field falls through to the next one, exactly like the pairs example.

    Complexity

    Sorting n elements takes O(n log n * C) time, where C is the cost of one comparison. Comparing two integers is constant time, so sorting numbers is O(n log n). Comparing two strings costs up to their length L, so sorting strings is O(n log n * L). The sort happens in place and uses O(1) extra space in the sense the lesson cares about, since it builds no copy of the data.

    The exercise, step by step

    1. Declare numbers as the slice []int{2, 1, 3, 10} with :=.
    2. Sort it in descending order with sort.Slice. The less function returns numbers[i] > numbers[j], so larger numbers come first. The result is [10 3 2 1].
    3. Declare coordinates as a [][]int with three pairs, []int{3, 2}, []int{2, 3} and []int{3, 5}, one per line, each followed by a comma. Go requires the comma after the last element when the closing brace is on its own line.
    4. Sort by the first value ascending, and by the second descending on ties. If the first values are equal, return coordinates[i][1] > coordinates[j][1]; otherwise return coordinates[i][0] < coordinates[j][0]. The result is [[2 3] [3 5] [3 2]].
    5. Declare names as []string{"Mircea", "john", "Andy"}.
    6. Sort alphabetically, ignoring case. The less function compares strings.ToLower(names[i]) with strings.ToLower(names[j]). Without the lowercasing, john would sort last. The result is [Andy john Mircea].
    numbers := []int{2, 1, 3, 10}
    sort.Slice(numbers, func(i int, j int) bool {
        return numbers[i] > numbers[j]
    })
    
    coordinates := [][]int{
        []int{3, 2},
        []int{2, 3},
        []int{3, 5},
    }
    sort.Slice(coordinates, func(i int, j int) bool {
        if coordinates[i][0] == coordinates[j][0] {
            return coordinates[i][1] > coordinates[j][1]
        }
        return coordinates[i][0] < coordinates[j][0]
    })
    
    names := []string{"Mircea", "john", "Andy"}
    sort.Slice(names, func(i int, j int) bool {
        return strings.ToLower(names[i]) < strings.ToLower(names[j])
    })

    The code uses the sort and strings packages, so a complete program imports both. An unused import is a compile error in Go.

    Key takeaways

    • sort.Ints and sort.Strings sort ascending; sort.Slice sorts by any rule you write.
    • The less function receives indices and returns true when element i goes before element j.
    • Tie-breaking comes from checking equality on the first key before comparing the second.
    • Sorting is in place, not stable by default (use sort.SliceStable), and costs O(n log n) comparisons.
    Keep going with GoAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free