Menu

Sort Slice di Golang: slices.Sort, SortFunc, dan Banyak Field

Urutkan slice di Go dengan slices.Sort dan slices.SortFunc, urutkan struct berdasarkan satu atau beberapa field dengan cmp.Compare, pertahankan urutan elemen yang sama dengan stable sort, dan pahami kode lama yang memakai sort.Slice.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

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.Sliceslices.SortFunc
Fungsi menerimaindeks i, jelemen a, b
Mengembalikanbool (apakah i lebih kecil dari j)int (negatif, nol, positif)
Keamanan tipemenerima any, memakai reflectiongeneric, diperiksa saat kompilasi
Kecepatanlebih lambatlebih 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 Sort mengembalikan slice yang sudah terurut. Fungsi ini mengurutkan di tempat dan tidak mengembalikan apa-apa. Pakai slices.Sorted(slices.Values(s)) kalau kamu ingin slice baru yang terurut, atau slices.Clone lebih dulu.
  • Mengira elemen yang sama tetap pada urutannya. Hanya varian Stable yang menjanjikan itu.
  • Membandingkan dengan pengurangan. a - b bisa overflow. Pakai cmp.Compare.
  • Mengurutkan float yang berisi NaN. cmp.Compare menempatkan NaN sebelum semua nilai lain, sehingga pengurutan tetap konsisten. Perbandingan a < b yang 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.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai