each dan kerabatnya

Metode each beserta each_with_index, each_slice, dan each_entry untuk iterasi yang rapi.

Daftar isi

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.

ruby each mengembalikan koleksinya sendiri
angka = [1, 2, 3]
hasil = angka.each { |n| puts "proses #{n}" }
p hasil
text Keluaran
proses 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.

ruby each_with_index menyertakan nomor posisi
peserta = ["Ali", "Budi", "Citra"]

peserta.each_with_index do |nama, urutan|
  puts "#{urutan + 1}. #{nama}"
end
text Keluaran
1. Ali
2. Budi
3. Citra

Penjumlahan 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.

ruby loket.rb
# 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(", ")}"
end
text Keluaran
Dilayani 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, 11

Sebelas 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.

ruby each_slice tanpa blok menghasilkan Enumerator
kelompok = (1..11).each_slice(4)
p kelompok
p kelompok.to_a
text Keluaran
#<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.

ruby each_entry menyampaikan satu objek per iterasi
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
text Keluaran
[:senin, 3]
[:selasa, 5]
Enumerator
Enumerator

Pada 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.

ruby Menambah elemen ke array yang sedang diiterasi
# 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.

ruby Mengira each mengumpulkan hasil blok
angka = [1, 2, 3]
p angka.each { |n| n * 2 }
text Keluaran
[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#

  • each menjalankan blok untuk setiap elemen, mengembalikan koleksinya sendiri, dan cocok untuk efek samping.
  • each_with_index menyampaikan elemen beserta indeksnya yang dimulai dari nol.
  • each_slice(n) menyampaikan kelompok berisi paling banyak n elemen, dan kelompok terakhir boleh lebih kecil.
  • each_entry menjanjikan tepat satu objek per iterasi dan mengembalikan Enumerator apabila blok tidak diberikan.

Referensi#