map, select, dan reject adalah tiga metode pengolahan koleksi yang paling sering digunakan dalam pekerjaan sehari-hari. Metode map mengumpulkan hasil transformasi setiap elemen, select menyimpan elemen yang lolos syarat, sedangkan reject menyimpan elemen yang tidak lolos. Topik ini membahas ketiganya, penggunaannya secara berantai sebagai pipeline, yaitu rangkaian pengolahan yang hasilnya menjadi masukan tahap berikutnya, serta versi berakhiran tanda seru yang mengubah array aslinya.
map untuk transformasi#
Metode map menjalankan blok untuk setiap elemen dan mengumpulkan hasilnya menjadi array baru. Jumlah elemen hasil sama dengan jumlah elemen asal, dan urutannya dipertahankan, sedangkan array asal tetap tidak berubah.
panjang = ["api", "buku", "lampu"]
p panjang.map { |kata| kata.length }
p panjang.map { |kata| kata.upcase }
p panjang[3, 4, 5]
["API", "BUKU", "LAMPU"]
["api", "buku", "lampu"]Baris ketiga keluaran menegaskan bahwa panjang tetap berisi kata aslinya, sehingga hasil map perlu ditampung dalam variabel apabila ingin digunakan. Isi blok bebas bentuknya, yaitu boleh menghasilkan bilangan, string, atau objek lain. Satu baris kode sudah mewakili perulangan yang memerlukan variabel penampung jika ditulis manual.
select dan reject untuk penyaringan#
Metode select mengembalikan array baru yang hanya memuat elemen yang lolos syarat blok, sedangkan reject mengembalikan array baru berisi elemen yang tidak lolos. Blok yang diberikan berperilaku seperti metode predicate, yaitu menghasilkan true atau false untuk setiap elemen, dan elemen yang menghasilkan true disebut lolos.
nilai = [70, 85, 60, 90, 75]
p nilai.select { |n| n >= 75 }
p nilai.reject { |n| n >= 75 }
p nilai[85, 90, 75]
[70, 60]
[70, 85, 60, 90, 75]Kedua metode mengembalikan hasil yang saling melengkapi, yaitu gabungan keduanya memuat seluruh elemen asal, sementara nilai tetap utuh. Ketiga metode ini juga bekerja pada Hash, dengan perbedaan bentuk hasilnya.
suhu = { jakarta: 31, bandung: 24 }
p suhu.map { |kota, derajat| "#{kota}: #{derajat}" }
p suhu.select { |kota, derajat| derajat >= 28 }
p suhu.reject { |kota, derajat| derajat >= 28 }["jakarta: 31", "bandung: 24"]
{jakarta: 31}
{bandung: 24}Pada Hash, metode map selalu mengembalikan array, sedangkan select dan reject mengembalikan Hash berisi pasangan-pasangan yang lolos syarat. Blok menerima kunci dan nilai sebagai dua variabel, misalnya |kota, derajat|.
Pipeline transformasi dan penyaringan#
Karena ketiga metode mengembalikan koleksi baru, hasil satu metode dapat langsung menjadi masukan metode berikutnya. Susunan semacam ini adalah pipeline yang dibaca dari kiri ke kanan sesuai urutan eksekusinya. Diagram berikut menggambarkan alurnya pada empat nilai pertama data ujian.
# Pipeline pengolahan nilai ujian: transformasi lalu penyaringan.
mentah = [70, 55, 88, 62, 91, 74]
dikurva = mentah.map { |n| n + 5 }
lulus = dikurva.select { |n| n >= 75 }
remedial = dikurva.reject { |n| n >= 75 }
p dikurva
p lulus
p remedial
rekap = {
jumlah: mentah.length,
lulus: lulus.length,
remedial: remedial.length
}
p rekap[75, 60, 93, 67, 96, 79]
[75, 93, 96, 79]
[60, 67]
{jumlah: 6, lulus: 4, remedial: 2}Tahap map menaikkan seluruh nilai sebanyak lima poin sebagai bentuk kurva, kemudian select dan reject membagi hasilnya menjadi dua kelompok menurut batas kelulusan. Penamaan variabel per tahap, yaitu mentah, dikurva, lulus, dan remedial, membuat pipeline terbaca seperti kalimat dan memudahkan penelusuran saat salah satu tahap perlu diperiksa.
Padanan bang! yang mengubah array asli#
Metode berakhiran tanda seru, seperti sort!, map!, select!, dan reject!, mengubah array aslinya secara langsung alih-alih mengembalikan salinan baru, dan versi ini dikenal sebagai metode bang. Versi ini menghemat pembuatan salinan, tetapi isi sebelumnya hilang tanpa dapat dikembalikan.
angka = [3, 1, 2]
angka.sort!
p angka
angka.map! { |n| n * 10 }
p angka[1, 2, 3]
[10, 20, 30]Perilaku select! dan reject! perlu dicermati, yaitu keduanya mengembalikan nil, bukan array aslinya, apabila tidak ada elemen yang berubah.
sisa = [90, 85]
hasil = sisa.select! { |n| n >= 75 }
p hasil
p sisanil
[90, 85]Kedua elemen lolos syarat sehingga tidak ada yang dihapus, dan variabel hasil menerima nil alih-alih array sisa. Akibatnya, pemanggilan berantai dengan versi bang dapat gagal tanpa diduga.
# tidak-dijalankan
sisa = [90, 85]
sisa.select! { |n| n >= 75 }.map! { |n| n + 5 }Ekspresi di atas memunculkan NoMethodError karena select! mengembalikan nil, dan nil tidak mengenal metode map!.
Kesalahan umum#
Kesalahan pertama adalah membangun array baru dengan each dan push padahal tujuannya transformasi. Pola berikut berfungsi, tetapi lebih panjang daripada yang diperlukan.
kuadrat = []
[1, 2, 3].each { |n| kuadrat.push(n * n) }
p kuadrat[1, 4, 9]Pola ini menuntut variabel penampung yang dideklarasikan terlebih dahulu, dan tujuannya baru terlihat setelah seluruh baris dibaca. Bentuk [1, 2, 3].map { |n| n * n } menghasilkan array yang sama dalam satu baris tanpa variabel penampung.
Kesalahan kedua adalah memanggil map tanpa menampung hasilnya, karena metode ini mengembalikan array baru dan membiarkan array asalnya utuh.
nilai = [70, 85]
nilai.map { |n| n + 5 }
p nilai[70, 85]Perhitungan berjalan, tetapi hasilnya dibuang begitu barisnya selesai, sehingga nilai tetap [70, 85]. Perbaikannya adalah menampung hasil dalam variabel, atau menggunakan map! apabila array aslinya memang ingin diubah.
Ringkasan#
mapmengumpulkan hasil blok setiap elemen menjadi array baru dengan jumlah dan urutan yang sama dengan asalnya.selectmenyimpan elemen yang lolos syarat blok,rejectmenyimpan yang tidak lolos, dan keduanya mengembalikan koleksi baru.- Hasil ketiganya dapat dirangkai menjadi pipeline yang terbaca dari kiri ke kanan.
- Versi bang seperti
map!danselect!mengubah array aslinya, danselect!mengembalikannilapabila tidak ada elemen yang berubah.
Referensi#
- Array#map — Ruby — Dokumentasi map beserta padanan map!-nya.
- Enumerable#select — Ruby — Dokumentasi select dan reject beserta perilaku kembaliannya.