Mengurutkan tipe bawaan
slices.Sort mengurutkan slice apa pun yang tipe elemennya bisa diurutkan: integer, float, dan string. Fungsi ini mengurutkan langsung di tempat dan tidak mengembalikan apa-apa.
Output:
[3 7 19 42 88]
[Bob Carl alice lisa]
true
2 true
String diurutkan berdasarkan byte, jadi setiap huruf besar ASCII muncul sebelum setiap huruf kecil. Untuk nama, hasil itu jarang sesuai harapan pengguna. Bagian berikutnya memperbaikinya.
Package slices hadir di Go 1.21. Sort miliknya adalah pattern-defeating quicksort: O(n log n), di tempat, dan tidak stabil.
Urutan sendiri dengan SortFunc
slices.SortFunc menerima fungsi pembanding func(a, b T) int. Kembalikan angka negatif jika a harus di depan, angka positif jika b yang harus di depan, dan nol jika keduanya sama. Helper cmp.Compare mengembalikan tepat itu untuk tipe yang bisa diurutkan.
strings.ToLower di dalam perbandingan mengalokasikan string baru setiap kali input-nya berisi huruf besar, dan perbandingan dijalankan sekitar n log n kali. Untuk slice besar, hitung key huruf kecilnya sekali saja. Untuk nama dalam bahasa lain (aksen, aturan locale), pakai golang.org/x/text/collate, yang berada di luar pustaka standar.
Fungsi pembanding harus konsisten: kalau ia bilang a di depan b, ia juga harus bilang b di belakang a. Fungsi yang melanggar aturan ini (misalnya mengembalikan -1 setiap kali dua nilai berbeda) menghasilkan slice dengan urutan salah tanpa error. Mengurangkan integer (return a - b) terlihat rapi, tetapi bisa overflow untuk nilai besar; pakai cmp.Compare.
Mengurutkan struct
Fungsi pembanding menerima elemen, jadi mengurutkan struct berdasarkan sebuah field memakai panggilan yang sama:
Karyawan dengan gaji yang sama bisa keluar dalam urutan apa pun di sini, karena SortFunc tidak stabil. Kalau kamu butuh urutan yang pasti, pecahkan seri dengan field lain atau pakai stable sort.
Mengurutkan berdasarkan beberapa field
Bandingkan field yang paling penting lebih dulu, dan lanjut ke field berikutnya hanya kalau hasilnya seri. cmp.Or (Go 1.22) mengembalikan argumen pertamanya yang bukan nol, sehingga semuanya jadi satu baris:
Output:
eng 150 Cy
eng 120 Ana
eng 120 Eve
sales 90 Bob
sales 90 Dee
Setiap perbandingan tetap dievaluasi walaupun perbandingan pertama sudah menentukan hasilnya, karena semuanya hanyalah argumen biasa. Untuk perbandingan field, biayanya kecil. Kalau sebuah pemecah seri mahal, tulis rangkaian if c := ...; c != 0 { return c } secara manual.
Stable sort
Stable sort menjaga elemen yang dianggap sama tetap dalam urutan aslinya. Hal ini penting ketika input sudah punya urutan yang bermakna, misalnya catatan yang diurutkan berdasarkan waktu lalu sekarang kamu kelompokkan per pengguna.
Program ini mencetak [{ana 2} {ana 4} {bob 1} {bob 3} {bob 5}]: di dalam setiap pengguna, urutan aslinya tetap terjaga. Stable sort bekerja lebih banyak, jadi pakai hanya kalau urutan elemen yang sama itu penting.
Mengurutkan map
Map tidak punya urutan. Untuk menampilkan map yang terurut berdasarkan key, urutkan key-nya: slices.Sorted(maps.Keys(m)) (Go 1.23). Untuk mengurutkan berdasarkan value, urutkan key-nya dengan perbandingan yang mencari value-nya:
Pemecah seri berdasarkan kata itu penting: tanpanya, chan dan slice (keduanya 7) akan tercetak dalam urutan yang berbeda di setiap run, karena key keluar dari map dalam urutan acak. Lihat map untuk penjelasan lebih lanjut.
Package sort: sort.Slice dan kawan-kawannya
Sebelum Go 1.21, pengurutan dilakukan lewat package sort. Kamu akan menemukannya di banyak kode yang sudah ada:
sort.Ints(nums)
sort.Strings(names)
sort.Slice(staff, func(i, j int) bool {
return staff[i].Salary < staff[j].Salary
})
sort.SliceStable(staff, func(i, j int) bool { ... })
Perbedaan yang perlu kamu tahu:
sort.Slice | slices.SortFunc | |
|---|---|---|
| Fungsi menerima | indeks i, j | elemen a, b |
| Mengembalikan | bool (apakah i lebih kecil dari j) | int (negatif, nol, positif) |
| Keamanan tipe | menerima any, memakai reflection | generic, diperiksa saat kompilasi |
| Kecepatan | lebih lambat | lebih cepat |
Bug yang umum pada sort.Slice adalah closure yang menangkap slice yang berbeda dari slice yang sedang diurutkan, karena fungsi less mengakses elemen berdasarkan posisi. SortFunc tidak mungkin punya bug itu karena ia langsung memberimu elemennya.
Tipe sort.Interface (Len, Less, Swap) adalah bentuk yang paling tua. Bentuk ini masih menjadi cara untuk mengurutkan data yang bukan satu slice tunggal, seperti dua slice paralel yang harus bergerak bersama. Sejak Go 1.22, sort.Ints, sort.Strings, dan sort.Float64s hanya memanggil slices.Sort.
Kesalahan umum
- Mengira
Sortmengembalikan slice yang sudah terurut. Fungsi ini mengurutkan di tempat dan tidak mengembalikan apa-apa. Pakaislices.Sorted(slices.Values(s))kalau kamu ingin slice baru yang terurut, atauslices.Clonelebih dulu. - Mengira elemen yang sama tetap pada urutannya. Hanya varian
Stableyang menjanjikan itu. - Membandingkan dengan pengurangan.
a - bbisa overflow. Pakaicmp.Compare. - Mengurutkan float yang berisi NaN.
cmp.Comparemenempatkan NaN sebelum semua nilai lain, sehingga pengurutan tetap konsisten. Perbandingana < byang ditulis manual tidak begitu.
Pertanyaan yang sering diajukan
Bagaimana cara mengurutkan slice di Go?
Untuk angka dan string, panggil slices.Sort(s) (Go 1.21). Fungsi ini mengurutkan langsung di tempat, dari kecil ke besar. Untuk tipe lain, atau urutan yang berbeda, pakai slices.SortFunc(s, func(a, b T) int { ... }), dengan fungsi yang mengembalikan angka negatif jika a di depan, positif jika b yang di depan, dan nol jika keduanya sama.
Bagaimana cara mengurutkan slice secara menurun di Go?
Tukar argumen di perbandingannya: slices.SortFunc(s, func(a, b int) int { return cmp.Compare(b, a) }). Atau urutkan naik, lalu panggil slices.Reverse(s).
Bagaimana cara mengurutkan slice of struct berdasarkan beberapa field di Go?
Bandingkan field pertama, dan lanjut ke field berikutnya hanya jika nilainya sama. cmp.Or (Go 1.22) melakukan tepat itu: return cmp.Or(cmp.Compare(a.Dept, b.Dept), cmp.Compare(b.Salary, a.Salary), strings.Compare(a.Name, b.Name)) mengembalikan hasil pertama yang bukan nol.
Apa bedanya sort.Slice dan slices.SortFunc?
sort.Slice(s, func(i, j int) bool) adalah API lama: menerima fungsi less atas indeks dan memakai reflection untuk menukar elemen. slices.SortFunc(s, func(a, b T) int) bersifat generic, diperiksa tipenya, menerima elemen secara langsung, dan lebih cepat. Kode baru sebaiknya memakai package slices; sort.Slice masih sering ditemukan di kode yang ditulis sebelum Go 1.21.