24. Bekerja dengan slice

Kamu sudah membuat slice dengan tangan dan menelusurinya dengan range. Pustaka standar Go juga punya package slices, dengan fungsi untuk pekerjaan umum: mengurutkan, membalik, mencari. Pelajari ini, dan kamu jarang perlu menulis perulangannya sendiri.

Tambahkan "slices" ke import-mu. Package ini butuh Go 1.21 atau lebih baru, yang sudah kamu dapatkan dari Bab 06. Sebagian besar alat di bab ini juga ada di exercises/24/02-slice-tools.go.

Mengurutkan slice: slices.Sort

slices.Sort menyusun ulang slice di tempat. Fungsi ini mengubah aslinya, dan tidak mengembalikan apa-apa:

scores := []int{30, 12, 7, 24}
slices.Sort(scores)
fmt.Println(scores) // [7 12 24 30]

Teks juga diurutkan menurut abjad:

names := []string{"Cara", "Ada", "Ben"}
slices.Sort(names)
fmt.Println(names) // [Ada Ben Cara]

Karena tidak mengembalikan apa-apa, tidak ada nilai yang bisa ditugaskan:

scores := []int{30, 12, 7, 24}
sorted := slices.Sort(scores)
fmt.Println(sorted)
slices.Sort(scores) (no value) used as value

Untuk mempertahankan urutan aslinya, urutkan salinannya. Ini make dan copy dari Bab 22:

scores := []int{30, 12, 7, 24}

ascending := make([]int, len(scores))
copy(ascending, scores)
slices.Sort(ascending)

fmt.Println(ascending) // [7 12 24 30]
fmt.Println(scores)    // [30 12 7 24]

Mengurutkan dengan caramu sendiri

Untuk dari tertinggi ke terendah, urutkan dulu, lalu balik:

scores := []int{30, 12, 7, 24}
slices.Sort(scores)
slices.Reverse(scores)
fmt.Println(scores) // [30 24 12 7]

Untuk mengurutkan berdasarkan hal lain, tulis fungsi yang membandingkan dua item dan berikan ke slices.SortFunc. Fungsi itu mengembalikan angka negatif kalau a lebih dulu, angka positif kalau b lebih dulu, dan 0 kalau keduanya sama. Ini contoh mengurutkan berdasarkan panjang:

func byLength(a string, b string) int {
    return len(a) - len(b)
}

func main() {
    words := []string{"banana", "fig", "apple", "kiwi"}
    slices.SortFunc(words, byLength)
    fmt.Println(words) // [fig kiwi apple banana]
}

Perhatikan bahwa byLength tidak memakai () di panggilan itu. Kamu menyerahkan fungsinya sendiri kepada slices.SortFunc, yang memanggilnya sesering yang diperlukan.

Buka exercises/24/01-sort.go. Slice berisi angka sudah disediakan. Urutkan dari terendah ke tertinggi dan cetak, lalu urutkan dari tertinggi ke terendah dan cetak lagi.

Membalik slice: slices.Reverse

slices.Reverse membalik slice di tempat:

items := []string{"a", "b", "c"}
slices.Reverse(items)
fmt.Println(items) // [c b a]

Untuk mendapat salinan terbalik tanpa mengubah aslinya, salin dulu:

items := []string{"a", "b", "c"}

flipped := make([]string, len(items))
copy(flipped, items)
slices.Reverse(flipped)

fmt.Println(flipped) // [c b a]
fmt.Println(items)   // [a b c]

Menggabungkan slice menjadi teks

strings.Join, dari package strings, menyatukan setiap item menjadi satu string, dengan pemisah di antaranya. Slice-nya ditulis lebih dulu, dan itemnya harus string:

fruits := []string{"apple", "banana", "cherry"}
fmt.Println(strings.Join(fruits, ", "))  // apple, banana, cherry
fmt.Println(strings.Join(fruits, " - ")) // apple - banana - cherry
fmt.Println(strings.Join(fruits, ""))    // applebananacherry

Untuk slice berisi angka, ubah dulu menjadi teks, dengan perulangan dan strconv.Itoa:

scores := []int{7, 12, 24, 30}

parts := []string{}
for _, score := range scores {
    parts = append(parts, strconv.Itoa(score))
}
fmt.Println(strings.Join(parts, " ")) // 7 12 24 30

Memberikan angkanya langsung tidak bisa dikompilasi:

cannot use scores (variable of type []int) as []string value in argument to strings.Join

Menyalin slice: copy

copy butuh slice tujuan dengan panjang yang tepat. make memberimu slice itu:

original := []int{1, 2, 3}

duplicate := make([]int, len(original))
copy(duplicate, original)
duplicate[0] = 99

fmt.Println(original)  // [1 2 3]
fmt.Println(duplicate) // [99 2 3]

Tanpa copy, menugaskan slice ke variabel lain memberimu nama kedua untuk item yang sama, seperti yang ditunjukkan Bab 22. copy hanya mengisi item sebanyak yang muat, jadi kalau slice barunya lebih pendek, sisanya tidak ikut.

Mencari: slices.Index dan slices.Contains

slices.Index memberikan posisi kecocokan pertama, atau -1 kalau tidak ada. Ini jawaban yang sama dengan strings.Index di Bab 14:

fruits := []string{"apple", "banana", "cherry", "banana"}
fmt.Println(slices.Index(fruits, "banana")) // 1
fmt.Println(slices.Index(fruits, "grape"))  // -1

slices.Contains memberikan true atau false, kalau kamu tidak butuh posisinya:

fruits := []string{"apple", "banana", "cherry", "banana"}

if slices.Contains(fruits, "grape") {
    fmt.Println("found")
} else {
    fmt.Println("not found")
}

Package ini tidak punya fungsi untuk menghitung kecocokan, tetapi menulisnya sendiri cukup singkat, memakai pola hitung dari Bab 20:

func countOf(items []string, value string) int {
    count := 0
    for _, item := range items {
        if item == value {
            count++
        }
    }
    return count
}

func main() {
    fruits := []string{"apple", "banana", "cherry", "banana"}
    fmt.Println(countOf(fruits, "banana")) // 2
    fmt.Println(countOf(fruits, "grape"))  // 0
}

Menghapus item berdasarkan nilai

Sekarang kamu bisa mencari item dan menghapusnya, dengan pemotongan dari Bab 22:

fruits := []string{"apple", "banana", "cherry"}

i := slices.Index(fruits, "banana")
if i != -1 {
    fruits = append(fruits[:i], fruits[i+1:]...)
}
fmt.Println(fruits) // [apple cherry]

i = slices.Index(fruits, "grape")
if i != -1 {
    fruits = append(fruits[:i], fruits[i+1:]...)
}
fmt.Println(fruits) // [apple cherry]

if itu penting. Tanpanya, slices.Index memberikan -1 untuk item yang tidak ada, fruits[:i] diminta berhenti di posisi -1, dan program crash:

panic: runtime error: slice bounds out of range [:-1]

Package ini juga punya slices.Insert, yang menambahkan item di posisi mana pun, dan slices.Delete, yang menghapus rentang posisi. Keduanya mengembalikan slice baru, jadi simpan hasilnya, seperti pada append:

fruits := []string{"apple", "cherry"}

fruits = slices.Insert(fruits, 1, "banana")
fmt.Println(fruits) // [apple banana cherry]

fruits = slices.Delete(fruits, 0, 1)
fmt.Println(fruits) // [banana cherry]

Membuat slice baru dengan perulangan

Untuk membuat slice baru dari slice lama, mulai dengan slice kosong dan pakai append di dalam perulangan range:

nums := []int{1, 2, 3, 4, 5}

doubled := []int{}
for _, n := range nums {
    doubled = append(doubled, n*2)
}
fmt.Println(doubled) // [2 4 6 8 10]

Tambahkan if untuk menyimpan sebagian item saja:

nums := []int{1, 2, 3, 4, 5}

evens := []int{}
for _, n := range nums {
    if n%2 == 0 {
        evens = append(evens, n)
    }
}
fmt.Println(evens) // [2 4]

Kebanyakan kode Go yang akan kamu baca melakukan persis hal ini. Butuh beberapa baris, tetapi setiap barisnya jelas.

PR

File PR ada di exercises/24/homework/. Setiap file bisa langsung dijalankan dan mencetak satu baris pengganti, dan komentar paling atasnya menyebutkan import yang kamu butuhkan.

Soal 1 — Daftar belanja

Buka exercises/24/homework/01-shopping-line.go. Slice berisi nama barang sudah disediakan. Cetak sebagai satu baris yang dipisahkan koma, memakai strings.Join.

Soal 2 — Tinggi ke rendah

Buka exercises/24/homework/02-high-to-low.go. Slice berisi angka sudah disediakan. Urutkan dari tertinggi ke terendah, lalu cetak dalam satu baris dengan strings.Join.

Soal 3 — Papan peringkat

Buka exercises/24/homework/03-leaderboard.go. Slice berisi nama pemain yang teracak sudah disediakan. Urutkan menurut abjad dan cetak papan peringkat bernomor, satu per baris (1. Ada, 2. Ben, dan seterusnya).

Tantangan — Tiga teratas

Buka exercises/24/homework/04-top-three.go. Slice berisi skor sudah disediakan. Urutkan dari tertinggi, lalu cetak tiga teratas saja dalam satu baris yang dipisahkan spasi. Pakai slices.Sort, slices.Reverse, dan strings.Join, ditambah pemotongan untuk mengambil tiga yang pertama.

Stuck atau sudah selesai? Buka halaman solusi PR.