Saya punya map
yang bisa mengubah nilai atau menyetelnya menjadi nol. Saya kemudian ingin menghapus entri nil dari daftar. Daftar tidak perlu disimpan.
Inilah yang saat ini saya miliki:
# A simple example function, which returns a value or nil
def transform(n)
rand > 0.5 ? n * 10 : nil }
end
items.map! { |x| transform(x) } # [1, 2, 3, 4, 5] => [10, nil, 30, 40, nil]
items.reject! { |x| x.nil? } # [10, nil, 30, 40, nil] => [10, 30, 40]
Saya sadar saya hanya bisa melakukan loop dan secara kondisional mengumpulkan di array lain seperti ini:
new_items = []
items.each do |x|
x = transform(x)
new_items.append(x) unless x.nil?
end
items = new_items
Tapi sepertinya itu tidak idiomatis. Apakah ada cara yang bagus untuk memetakan fungsi pada daftar, menghapus / mengecualikan nil saat Anda pergi?
filter_map
, yang tampaknya sempurna untuk ini. Menghemat kebutuhan untuk memproses ulang array, alih-alih mendapatkannya sesuai keinginan pertama kali. Info lebih lanjut di sini.Jawaban:
Ruby 2.7+
Ada sekarang!
Ruby 2.7 memperkenalkan
filter_map
untuk tujuan yang tepat ini. Ini idiomatis dan pemain, dan saya berharap itu akan menjadi norma segera.Sebagai contoh:
Dalam kasus Anda, ketika blok mengevaluasi ke falsey, cukup:
" Ruby 2.7 menambahkan Enumerable # filter_map " adalah bacaan yang bagus tentang subjek, dengan beberapa tolok ukur kinerja terhadap beberapa pendekatan sebelumnya untuk masalah ini:
sumber
Anda bisa menggunakan
compact
:Saya ingin mengingatkan orang bahwa jika Anda mendapatkan array yang berisi nils sebagai output dari sebuah
map
blok, dan blok itu mencoba mengembalikan nilai secara kondisional, maka Anda memiliki kode bau dan perlu memikirkan kembali logika Anda.Misalnya, jika Anda melakukan sesuatu yang melakukan ini:
Kalau begitu jangan. Alih-alih, sebelum
map
,reject
hal-hal yang tidak Anda inginkan atauselect
apa yang Anda inginkan:Saya menganggap menggunakan
compact
untuk membersihkan kekacauan sebagai upaya terakhir untuk menyingkirkan hal-hal yang tidak kami tangani dengan benar, biasanya karena kami tidak tahu apa yang akan terjadi pada kami. Kita harus selalu tahu data seperti apa yang dilemparkan ke dalam program kita; Data yang tidak terduga / tidak dikenal buruk. Setiap kali saya melihat nils dalam array yang sedang saya kerjakan, saya menggali mengapa mereka ada, dan melihat apakah saya dapat meningkatkan kode yang menghasilkan array, daripada membiarkan Ruby membuang-buang waktu dan memori yang menghasilkan nil kemudian memilah-milah array untuk menghapus mereka nanti.sumber
nil
entri, bukan string kosong. BTW,nil
tidak sama dengan string kosong.reduce
atauinject
?compact
lebih cepat tetapi sebenarnya menulis kode dengan benar di awal menghilangkan kebutuhan untuk menangani nils sepenuhnya.Coba gunakan
reduce
atauinject
.Saya setuju dengan jawaban yang diterima bahwa kita tidak boleh
map
dancompact
, tetapi tidak untuk alasan yang sama.Saya merasa jauh di lubuk hati yang
map
kemudiancompact
setara denganselect
itumap
. Pertimbangkan:map
adalah fungsi satu-ke-satu. Jika Anda memetakan dari beberapa set nilai, dan Andamap
, maka Anda ingin satu nilai dalam set output untuk setiap nilai dalam set input. Jika Anda harusselect
sebelumnya, maka Anda mungkin tidak inginmap
di set. Jika Anda harusselect
setelah itu (ataucompact
) maka Anda mungkin tidak inginmap
di set. Dalam kedua kasus Anda mengulangi dua kali seluruh set, ketikareduce
hanya perlu pergi sekali.Juga, dalam bahasa Inggris, Anda mencoba "mengurangi satu set bilangan bulat menjadi satu set bilangan bulat genap".
sumber
Dalam contoh Anda:
itu tidak terlihat seperti nilai telah berubah selain diganti dengan
nil
. Jika itu masalahnya, maka:akan cukup.
sumber
Jika Anda ingin kriteria yang lebih longgar untuk penolakan, misalnya, untuk menolak string kosong dan nol, Anda dapat menggunakan:
Jika Anda ingin melangkah lebih jauh dan menolak nilai nol (atau menerapkan logika yang lebih kompleks untuk proses tersebut), Anda bisa meneruskan blok untuk menolak:
sumber
blank?
hanya tersedia di rel, kita bisa menggunakanitems.reject!(&:nil?) # [1, nil, 3, nil, nil] => [1, 3]
yang tidak digabungkan ke rel. (tidak akan mengecualikan string atau 0s kosong)Jelas
compact
merupakan pendekatan terbaik untuk menyelesaikan tugas ini. Namun, kami dapat mencapai hasil yang sama hanya dengan pengurangan sederhana:sumber
each_with_object
mungkin cara paling bersih untuk pergi ke sini:Menurut pendapat saya,
each_with_object
lebih baik daripadainject
/reduce
dalam kasus bersyarat karena Anda tidak perlu khawatir tentang nilai pengembalian blok.sumber
Satu cara lagi untuk mencapainya adalah seperti yang ditunjukkan di bawah ini. Di sini, kami menggunakan
Enumerable#each_with_object
untuk mengumpulkan nilai-nilai, dan memanfaatkanObject#tap
untuk menyingkirkan variabel sementara yang jika tidak diperlukan untuknil
memeriksa hasilprocess_x
metode.Contoh lengkap untuk ilustrasi:
Pendekatan alternatif:
Dengan melihat metode yang Anda panggil
process_x url
, tidak jelas apa tujuan inputx
dalam metode itu. Jika saya berasumsi bahwa Anda akan memproses nilaix
dengan melewatkannyaurl
dan menentukan mana darix
s yang benar-benar diproses menjadi hasil non-nil yang valid - maka, mungkinEnumerabble.group_by
merupakan opsi yang lebih baik daripadaEnumerable#map
.sumber