Глиф Учебники

Как отсортировать срез

Справочник · Go · 2 минуты чтения

Go
package main

import (
    "fmt"
    "slices"
    "sort"
)

func main() {
    a := []int{5, 2, 8, 1}
    sort.Ints(a)
    fmt.Println(a)

    b := []int{5, 2, 8, 1}
    slices.Sort(b)
    slices.Reverse(b)
    fmt.Println(b)

    words := []string{"груша", "яблоко", "арбуз"}
    sort.Strings(words)
    fmt.Println(words)
}
Вывод
[1 2 5 8]
[8 5 2 1]
[арбуз груша яблоко]

Сортировка меняет сам срез и ничего не возвращает. Писать a = sort.Ints(a) не нужно — так и не соберётся.

Срез структур по полю

Для структур указывают функцию сравнения. В slices.SortFunc и slices.SortStableFunc она получает два элемента и возвращает отрицательное число, если первый должен идти раньше, ноль — если они равны, положительное — если позже. Готовая такая функция для чисел и строк — cmp.Compare:

Go
package main

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

type Student struct {
    Name  string
    Score int
}

func main() {
    students := []Student{
        {Name: "Анна", Score: 90},
        {Name: "Борис", Score: 75},
        {Name: "Вера", Score: 90},
    }
    slices.SortStableFunc(students, func(a, b Student) int {
        return cmp.Compare(b.Score, a.Score)
    })
    for _, s := range students {
        fmt.Println(s.Name, s.Score)
    }
}
Вывод
Анна 90
Вера 90
Борис 75

cmp.Compare(b.Score, a.Score) — b и a переставлены, поэтому сортировка идёт по убыванию баллов. Устойчивая сортировка (Stable) сохраняет исходный порядок равных элементов: Анна была раньше Веры и осталась раньше. Обычная slices.SortFunc такого не обещает.

Сортировка по двум полям — сначала по баллам, при равенстве по имени:

Go
slices.SortFunc(students, func(a, b Student) int {
    if c := cmp.Compare(b.Score, a.Score); c != 0 {
        return c
    }
    return cmp.Compare(a.Name, b.Name)
})

sort.Slice

В старом коде и в версиях Go до 1.21 то же делают через sort.Slice. Функция получает номера элементов и отвечает, должен ли i-й идти раньше j-го:

Go
sort.Slice(students, func(i, j int) bool {
    return students[i].Score > students[j].Score
})

Устойчивый вариант — sort.SliceStable.

Русские буквы

Строки сравниваются по байтам кодировки UTF-8. Для строчных русских букв без «ё» это совпадает с алфавитом, но есть исключения:

Go
package main

import (
    "fmt"
    "slices"
)

func main() {
    words := []string{"ёж", "яма", "арбуз", "Арбуз"}
    slices.Sort(words)
    fmt.Println(words)
}
Вывод
[Арбуз арбуз яма ёж]

Для сортировки без учёта регистра (больших и маленьких букв) сравнивайте строки, приведённые к одному регистру: strings.ToLower(a). Правильный русский алфавитный порядок, с «ё» рядом с «е», даёт пакет golang.org/x/text/collate — он из официальных дополнительных библиотек Go, ставится через go get.

Подводные камни