Metode each menjalankan sebuah blok untuk setiap elemen koleksi dan menjadi dasar bagi metode-metode iterasi yang menyertainya. Topik ini membahas each beserta kerabat dekatnya, yaitu each_with_index untuk iterasi bernomor, each_slice untuk iterasi per kelompok, dan each_entry dengan kontrak satu objek per elemen. Setelah membacanya, bentuk iterasi koleksi dapat disesuaikan dengan bentuk datanya tanpa mengelola variabel indeks secara manual.
Blok dan mekanika each#
Sebuah blok (block) adalah potongan kode di antara do dan end, atau di antara tanda kurung kurawal, yang diserahkan kepada metode untuk dijalankan. Metode each menjalankan blok sebanyak jumlah elemen dan menyampaikan satu elemen kepada variabel blok pada setiap iterasi, misalnya |nomor|. Pengaturan indeks, batas, dan kenaikan tidak ditulis karena semuanya ditangani metodenya.
Nilai kembalian each juga perlu dikenali sejak awal, yaitu koleksinya sendiri dan bukan hasil blok.
angka = [1, 2, 3]
hasil = angka.each { |n| puts "proses #{n}" }
p hasilproses 1
proses 2
proses 3
[1, 2, 3]Blok tetap berjalan tiga kali, tetapi nilai yang dikembalikan each adalah angka sebagaimana aslinya. Karena itu, each digunakan untuk efek samping, yaitu mencetak baris, mengubah keadaan di luar koleksi, atau mengisi struktur lain, dan bukan untuk mengumpulkan hasil per elemen.
each_with_index untuk iterasi bernomor#
Metode each_with_index menyampaikan dua nilai kepada blok pada setiap iterasi, yaitu elemennya dan indeks posisinya. Bloknya menerima dua variabel, misalnya |nama, urutan|, dengan indeks dimulai dari nol sebagaimana pengindeksan array pada umumnya.
peserta = ["Ali", "Budi", "Citra"]
peserta.each_with_index do |nama, urutan|
puts "#{urutan + 1}. #{nama}"
end1. Ali
2. Budi
3. CitraPenjumlahan urutan + 1 mengubah indeks dari nol menjadi nomor urut yang mudah dibaca manusia. Pola ini berguna ketika nomor posisi hanya diperlukan untuk tampilan, misalnya daftar peserta atau peringkat, tanpa perlu membuat variabel penghitung di luar perulangan.
each_slice untuk iterasi per kelompok#
Metode each_slice(n) membagi koleksi menjadi kelompok berisi paling banyak n elemen dan menyampaikan setiap kelompok sebagai array kepada blok. Kelompok terakhir boleh lebih kecil apabila sisa elemennya kurang dari n. Program berikut membandingkan pelayanan satu per satu dengan pembagian per jendela loket.
# Pembagian tugas loket berdasarkan nomor antrean.
antrean = (1..11).to_a
puts "Dilayani satu per satu:"
antrean.each do |nomor|
puts "Nomor #{nomor} silakan ke loket"
end
puts ""
puts "Dibagi per jendela:"
antrean.each_slice(4) do |kelompok|
puts "Jendela melayani nomor #{kelompok.join(", ")}"
endDilayani satu per satu:
Nomor 1 silakan ke loket
Nomor 2 silakan ke loket
Nomor 3 silakan ke loket
Nomor 4 silakan ke loket
Nomor 5 silakan ke loket
Nomor 6 silakan ke loket
Nomor 7 silakan ke loket
Nomor 8 silakan ke loket
Nomor 9 silakan ke loket
Nomor 10 silakan ke loket
Nomor 11 silakan ke loket
Dibagi per jendela:
Jendela melayani nomor 1, 2, 3, 4
Jendela melayani nomor 5, 6, 7, 8
Jendela melayani nomor 9, 10, 11Sebelas nomor dengan ukuran kelompok empat menghasilkan tiga kelompok, dan kelompok terakhir hanya berisi tiga nomor. Variabel kelompok berupa array utuh, sehingga metode array seperti join dapat langsung digunakan di dalam blok. Apabila blok tidak diberikan, each_slice mengembalikan Enumerator yang siap diiterasi kemudian.
kelompok = (1..11).each_slice(4)
p kelompok
p kelompok.to_a#<Enumerator: 1..11:each_slice(4)>
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]each_entry dan kontrak satu elemen#
Metode each_entry menjalankan blok untuk setiap elemen sebagaimana each, dengan janji bahwa setiap iterasi menyampaikan tepat satu objek. Pada koleksi yang setiap iterasinya menyampaikan lebih dari satu nilai, nilai-nilai itu dibungkus menjadi satu array agar blok tetap menerima satu objek. Hash memperlihatkan perilaku ini, karena satu pasangannya disampaikan sebagai satu objek berupa array.
rekap = { senin: 3, selasa: 5 }
rekap.each_entry { |entri| p entri }
e1 = [1, 2, 3].each_entry
p e1.class
e2 = [1, 2, 3].each
p e2.class[:senin, 3]
[:selasa, 5]
Enumerator
EnumeratorPada array biasa, hasil each_entry sama dengan each, dan keduanya mengembalikan Enumerator apabila blok tidak diberikan. Perbedaannya terletak pada kontrak jumlah objek per iterasi yang selalu satu, sehingga each_entry aman digunakan ketika kode ditulis secara generik untuk Enumerable apa pun tanpa mengetahui bentuk koleksinya. Blok tetap boleh menjabarkan pasangan dengan dua variabel, misalnya |kunci, nilai|, apabila isinya memang sepasang.
Kesalahan umum#
Kesalahan pertama adalah mengubah koleksi yang sedang diiterasi. Menambah elemen ke dalam array yang sedang diiterasi each membuat target iterasi terus bertambah, sehingga perulangannya tidak pernah berakhir.
# tidak-dijalankan
# Perulangan berikut tidak pernah berakhir karena array terus bertambah.
angka = [1, 2]
angka.each { |n| angka.push(n + 1) }Setiap iterasi menambah satu elemen baru di ujung array, sehingga sisa elemen tidak pernah habis dan program terjebak dalam perulangan tanpa menampilkan pesan apa pun.
Kesalahan kedua adalah mengandalkan nilai kembalian each sebagai hasil transformasi. Blok pada contoh berikut menghitung kelipatan dua, tetapi hasil perhitungannya dibuang karena each mengembalikan koleksinya sendiri.
angka = [1, 2, 3]
p angka.each { |n| n * 2 }[1, 2, 3]Keluaran tetap [1, 2, 3] karena tidak ada yang mengumpulkan hasil n * 2. Untuk membangun array baru dari hasil transformasi, Ruby menyediakan metode map yang memang mengumpulkan hasil blok menjadi array baru.
Ringkasan#
eachmenjalankan blok untuk setiap elemen, mengembalikan koleksinya sendiri, dan cocok untuk efek samping.each_with_indexmenyampaikan elemen beserta indeksnya yang dimulai dari nol.each_slice(n)menyampaikan kelompok berisi paling banyak n elemen, dan kelompok terakhir boleh lebih kecil.each_entrymenjanjikan tepat satu objek per iterasi dan mengembalikan Enumerator apabila blok tidak diberikan.
Referensi#
- Enumerable#each_with_index — Ruby — Dokumentasi each_with_index beserta perilaku indeksnya.
- Enumerable#each_slice — Ruby — Dokumentasi each_slice beserta perilaku Enumerator tanpa blok.
- Enumerable#each_entry — Ruby — Dokumentasi each_entry dan kontrak satu objek per elemen.