map, select, reject

Pipeline transformasi data dengan map, penyaringan select dan reject, beserta padanan bang!-nya.

Daftar isi

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.

ruby map mengumpulkan hasil blok
panjang = ["api", "buku", "lampu"]

p panjang.map { |kata| kata.length }
p panjang.map { |kata| kata.upcase }
p panjang
text Keluaran
[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.

ruby select dan reject sebagai dua sisi penyaringan
nilai = [70, 85, 60, 90, 75]

p nilai.select { |n| n >= 75 }
p nilai.reject { |n| n >= 75 }
p nilai
text Keluaran
[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.

ruby map, select, dan reject pada Hash
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 }
text Keluaran
["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.

data mentah[70, 55, 88, 62]map { n + 5 }[75, 60, 93, 67]select { n >= 75 }[75, 93]Nilai pada diagram adalah empat nilai pertama dari data ujian.
Alur pipeline map dan select pada data nilai ujian
ruby pipeline-nilai.rb
# 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
text Keluaran
[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.

ruby map! dan sort! mengubah array aslinya
angka = [3, 1, 2]
angka.sort!
p angka
angka.map! { |n| n * 10 }
p angka
text Keluaran
[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.

ruby select! yang tidak menghapus apa pun mengembalikan nil
sisa = [90, 85]
hasil = sisa.select! { |n| n >= 75 }
p hasil
p sisa
text Keluaran
nil
[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.

ruby Pemanggilan berantai versi bang yang gagal
# 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.

ruby Transformasi dengan each dan push
kuadrat = []
[1, 2, 3].each { |n| kuadrat.push(n * n) }
p kuadrat
text Keluaran
[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.

ruby Hasil map yang tidak ditampung
nilai = [70, 85]
nilai.map { |n| n + 5 }
p nilai
text Keluaran
[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#

  • map mengumpulkan hasil blok setiap elemen menjadi array baru dengan jumlah dan urutan yang sama dengan asalnya.
  • select menyimpan elemen yang lolos syarat blok, reject menyimpan yang tidak lolos, dan keduanya mengembalikan koleksi baru.
  • Hasil ketiganya dapat dirangkai menjadi pipeline yang terbaca dari kiri ke kanan.
  • Versi bang seperti map! dan select! mengubah array aslinya, dan select! mengembalikan nil apabila tidak ada elemen yang berubah.

Referensi#