Tolong jelaskan jika Anda ingin bermutasi objek string asli, hanya bermutasi yang asli, atau bermutasi tidak ada.
Phrogz
1
Maka Anda telah menerima jawaban yang salah. (Jangan tersinggung dengan @pst yang dimaksudkan, karena saya secara pribadi juga menganjurkan pemrograman gaya fungsional alih-alih objek bermutasi.)
Phrogz
Tapi tetap saja pendekatannya bagus
theReverseFlick
Jawaban:
178
Jika Anda ingin string yang sebenarnya bermutasi di tempat (mungkin dan diinginkan mempengaruhi referensi lain ke objek string yang sama):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|_,str| str.gsub!/^|$/,'%'}
my_hash.each{|_,str| str.replace "%#{str}%"}
Jika Anda ingin hash berubah di tempat, tetapi Anda tidak ingin memengaruhi string (Anda ingin mendapatkan string baru):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|key,str| my_hash[key]="%#{str}%"}
my_hash.inject(my_hash){|h,(k,str)| h[k]="%#{str}%"; h }
@ Andrew Marshall Benar, terima kasih. Di Ruby 1.8, Hash.[]tidak menerima array dari pasangan array, itu membutuhkan sejumlah argumen langsung (karena itu percikan di depan).
Phrogz
2
Sebenarnya, Hash. [Key_value_pairs] diperkenalkan di 1.8.7, jadi hanya Ruby 1.8.6 yang tidak membutuhkan gambar percikan & perataan.
Marc-André Lafortune
2
@Aupajo Hash#eachmenghasilkan kunci dan nilai pada blok. Dalam hal ini, saya tidak peduli dengan kuncinya, jadi saya tidak menyebutkan apa pun yang bermanfaat. Nama variabel dapat dimulai dengan garis bawah, dan pada kenyataannya mungkin hanya garis bawah. Tidak ada manfaat kinerja melakukan ini, itu hanya catatan mendokumentasikan diri sendiri yang halus bahwa saya tidak melakukan apa pun dengan nilai blok pertama itu.
Phrogz
1
Saya pikir maksud Anda my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, harus mengembalikan hash dari blok
aceofspades
1
Sebagai alternatif, Anda dapat menggunakan metode Each_value, yang sedikit lebih mudah dipahami daripada menggunakan garis bawah untuk nilai kunci yang tidak digunakan.
Strand McCutchen
266
Di Ruby 2.1 dan yang lebih tinggi dapat Anda lakukan
Terima kasih, sebenarnya apa yang saya cari. Tidak tahu mengapa Anda tidak terlalu dibesarkan.
simperreault
7
Ini, meskipun, sangat lambat dan sangat lapar RAM. Input Hash diulang untuk menghasilkan seperangkat Array bersarang antara yang kemudian dikonversi menjadi Hash baru. Mengabaikan penggunaan puncak RAM, run time jauh lebih buruk - pembandingan ini dibandingkan solusi modifikasi di tempat dalam jawaban lain menunjukkan 2,5s versus 1,5s pada jumlah iterasi yang sama. Karena Ruby adalah bahasa yang relatif lambat, menghindari sedikit bahasa lambat menjadi lambat :-)
Andrew Hodgkinson
2
@AndrewHodgkinson sementara secara umum saya setuju dan saya tidak menganjurkan tidak memperhatikan kinerja runtime, tidak melacak semua jebakan kinerja ini mulai menjadi sakit dan bertentangan dengan filosofi "produktivitas pertama pengembang" rubi? Saya kira ini kurang dari komentar untuk Anda, dan lebih dari komentar umum pada paradoks akhirnya membawa kita ke, menggunakan ruby.
elsurudo
4
Masalahnya adalah: yah, kami sudah menyerah kinerja dalam keputusan kami untuk menggunakan ruby, jadi apa bedanya "sedikit ini"? Ini lereng yang licin, bukan? Sebagai catatan, saya lebih suka solusi ini daripada jawaban yang diterima, dari perspektif keterbacaan.
elsurudo
1
Jika Anda menggunakan Ruby 2.4+, itu bahkan lebih mudah digunakan #transform_values!seperti yang ditunjukkan oleh sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Tidak sepenuhnya benar: string baru dialokasikan. Meski begitu, solusi menarik yang efektif. +1
Phrogz
@Phrogz poin bagus; Saya memperbarui jawabannya. Alokasi nilai tidak dapat dihindari secara umum karena tidak semua transformasi nilai dapat dinyatakan sebagai mutator seperti gsub!.
Sim
3
Sama seperti jawaban saya tetapi dengan sinonim lain, saya setuju bahwa updatemenyampaikan maksud lebih baik daripada merge!. Saya pikir ini adalah jawaban terbaik.
1
Jika Anda tidak menggunakan k, gunakan _saja.
sekrett
28
Sedikit lebih mudah dibaca, mapke array hash elemen tunggal dan reducedenganmerge
Lebih jelas untuk menggunakanHash[the_hash.map { |key,value| [key, "%#{value}%"] }]
meagar
Ini adalah cara yang sangat tidak efisien untuk memperbarui nilai. Untuk setiap pasangan nilai, pertama-tama ia menciptakan pasangan Array(untuk map) lalu a Hash. Kemudian, setiap langkah operasi pengurangan akan menduplikasi "memo" Hashdan menambahkan pasangan nilai kunci yang baru ke dalamnya. Setidaknya digunakan :merge!dalam reducememodifikasi final Hashdi tempat. Dan pada akhirnya, Anda tidak mengubah nilai-nilai objek yang ada tetapi membuat objek baru, yang bukan pertanyaannya.
Salah satu metode yang tidak memperkenalkan efek samping ke aslinya:
h ={:a =>'a',:b =>'b'}
h2 =Hash[h.map {|k,v|[k,'%'+ v +'%']}]
Peta Hash # juga dapat menjadi bacaan yang menarik karena menjelaskan mengapa Hash.mapHash tidak mengembalikan Hash (itulah sebabnya Array [key,value]pasangan yang dihasilkan dikonversi menjadi Hash baru) dan memberikan pendekatan alternatif untuk pola umum yang sama.
Selamat coding.
[Penafian: Saya tidak yakin apakah Hash.mapsemantik berubah di Ruby 2.x]
Saya tidak suka efek samping, tetapi +1 untuk pendekatan :) Ada each_with_objectdi Ruby 1.9 (IIRC) yang menghindari perlu mengakses nama secara langsung dan Map#mergemungkin juga berfungsi. Tidak yakin bagaimana detail rumitnya berbeda.
1
Hash awal diubah - tidak apa-apa jika perilaku diantisipasi tetapi dapat menyebabkan masalah halus jika "dilupakan". Saya lebih suka mengurangi mutabilitas objek, tetapi mungkin tidak selalu praktis. (Ruby bukan bahasa "bebas efek samping" ;-)
8
Hash.merge! adalah solusi terbersih
o ={ a:'a', b:'b'}
o.merge!(o){|key, value|"%#{ value }%"}
puts o.inspect
>{:a =>"%a%",:b =>"%b%"}
Jawaban:
Jika Anda ingin string yang sebenarnya bermutasi di tempat (mungkin dan diinginkan mempengaruhi referensi lain ke objek string yang sama):
Jika Anda ingin hash berubah di tempat, tetapi Anda tidak ingin memengaruhi string (Anda ingin mendapatkan string baru):
Jika Anda ingin hash baru:
sumber
Hash.[]
tidak menerima array dari pasangan array, itu membutuhkan sejumlah argumen langsung (karena itu percikan di depan).Hash#each
menghasilkan kunci dan nilai pada blok. Dalam hal ini, saya tidak peduli dengan kuncinya, jadi saya tidak menyebutkan apa pun yang bermanfaat. Nama variabel dapat dimulai dengan garis bawah, dan pada kenyataannya mungkin hanya garis bawah. Tidak ada manfaat kinerja melakukan ini, itu hanya catatan mendokumentasikan diri sendiri yang halus bahwa saya tidak melakukan apa pun dengan nilai blok pertama itu.my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, harus mengembalikan hash dari blokDi Ruby 2.1 dan yang lebih tinggi dapat Anda lakukan
sumber
#transform_values!
seperti yang ditunjukkan oleh sschmeck ( stackoverflow.com/a/41508214/6451879 ).Ruby 2.4 memperkenalkan metode
Hash#transform_values!
yang dapat Anda gunakan.sumber
Hash#transform_values
(tanpa bang), yang tidak memodifikasi receiver. Kalau tidak, jawaban yang bagus, terima kasih!reduce
:-pCara terbaik untuk memodifikasi nilai-nilai Hash di tempat adalah
Lebih sedikit kode dan maksud yang jelas. Juga lebih cepat karena tidak ada objek baru yang dialokasikan di luar nilai yang harus diubah.
sumber
gsub!
.update
menyampaikan maksud lebih baik daripadamerge!
. Saya pikir ini adalah jawaban terbaik.k
, gunakan_
saja.Sedikit lebih mudah dibaca,
map
ke array hash elemen tunggal danreduce
denganmerge
sumber
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(untukmap
) lalu aHash
. Kemudian, setiap langkah operasi pengurangan akan menduplikasi "memo"Hash
dan menambahkan pasangan nilai kunci yang baru ke dalamnya. Setidaknya digunakan:merge!
dalamreduce
memodifikasi finalHash
di tempat. Dan pada akhirnya, Anda tidak mengubah nilai-nilai objek yang ada tetapi membuat objek baru, yang bukan pertanyaannya.nil
jikathe_hash
kosongAda metode 'Rails way' baru untuk tugas ini :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
sumber
Hash#transform_values
. Ini harus menjadi cara untuk pergi dari sekarang.Salah satu metode yang tidak memperkenalkan efek samping ke aslinya:
Peta Hash # juga dapat menjadi bacaan yang menarik karena menjelaskan mengapa
Hash.map
Hash tidak mengembalikan Hash (itulah sebabnya Array[key,value]
pasangan yang dihasilkan dikonversi menjadi Hash baru) dan memberikan pendekatan alternatif untuk pola umum yang sama.Selamat coding.
[Penafian: Saya tidak yakin apakah
Hash.map
semantik berubah di Ruby 2.x]sumber
Hash.map
semantik berubah di Ruby 2.x?sumber
each_with_object
di Ruby 1.9 (IIRC) yang menghindari perlu mengakses nama secara langsung danMap#merge
mungkin juga berfungsi. Tidak yakin bagaimana detail rumitnya berbeda.Hash.merge! adalah solusi terbersih
sumber
Setelah mengujinya dengan RSpec seperti ini:
Anda dapat menerapkan Hash # map_values sebagai berikut:
Fungsi kemudian dapat digunakan seperti ini:
sumber
Jika Anda penasaran varian inplace mana yang tercepat di sini adalah:
sumber