Как отсортировать срез
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) не нужно — так и не соберётся.
sort.Ints,sort.Float64s,sort.Strings— старый пакетsort, есть во всех версиях Go.slices.Sort— пакетslices(Go 1.21 и новее): одна функция для срезов любых чисел и строк.- По убыванию проще всего отсортировать и развернуть:
slices.Sort(b), затемslices.Reverse(b).
Срез структур по полю
Для структур указывают функцию сравнения. В slices.SortFunc и slices.SortStableFunc она получает два элемента и возвращает отрицательное число, если первый должен идти раньше, ноль — если они равны, положительное — если позже. Готовая такая функция для чисел и строк — cmp.Compare:
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
Борис 75cmp.Compare(b.Score, a.Score) — b и a переставлены, поэтому сортировка идёт по убыванию баллов. Устойчивая сортировка (Stable) сохраняет исходный порядок равных элементов: Анна была раньше Веры и осталась раньше. Обычная slices.SortFunc такого не обещает.
Сортировка по двум полям — сначала по баллам, при равенстве по имени:
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-го:
sort.Slice(students, func(i, j int) bool {
return students[i].Score > students[j].Score
})Устойчивый вариант — sort.SliceStable.
Русские буквы
Строки сравниваются по байтам кодировки UTF-8. Для строчных русских букв без «ё» это совпадает с алфавитом, но есть исключения:
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.
Подводные камни
- Сортировка map. У map нет порядка — сортируют срез её ключей (пример — в уроке Map — словари в Go).
- Функция сравнения должна быть согласованной. Если она возвращает случайный или противоречивый результат, порядок получится непредсказуемым.
- Проверить, отсортирован ли срез, —
slices.IsSorted(s)илиsort.IntsAreSorted(s). - Найти в отсортированном срезе —
slices.BinarySearch(s, x): она возвращает позицию и признак, найдено ли.