Saya ingin mengubah setiap nilai dalam hash untuk menambahkan '%' sebelum dan sesudah nilai demikian
{ :a=>'a' , :b=>'b' }
harus diubah menjadi
{ :a=>'%a%' , :b=>'%b%' }
Apa cara terbaik untuk melakukan ini?
Saya ingin mengubah setiap nilai dalam hash untuk menambahkan '%' sebelum dan sesudah nilai demikian
{ :a=>'a' , :b=>'b' }
harus diubah menjadi
{ :a=>'%a%' , :b=>'%b%' }
Apa cara terbaik untuk melakukan ini?
Jawaban:
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 }
Jika Anda ingin hash baru:
# Ruby 1.8.6+
new_hash = Hash[*my_hash.map{|k,str| [k,"%#{str}%"] }.flatten]
# Ruby 1.8.7+
new_hash = Hash[my_hash.map{|k,str| [k,"%#{str}%"] } ]
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 blok
Di Ruby 2.1 dan yang lebih tinggi dapat Anda lakukan
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
seperti yang ditunjukkan oleh sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Ruby 2.4 memperkenalkan metode Hash#transform_values!
yang dapat Anda gunakan.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(tanpa bang), yang tidak memodifikasi receiver. Kalau tidak, jawaban yang bagus, terima kasih!
Cara terbaik untuk memodifikasi nilai-nilai Hash di tempat adalah
hash.update(hash){ |_,v| "%#{v}%" }
Lebih sedikit kode dan maksud yang jelas. Juga lebih cepat karena tidak ada objek baru yang dialokasikan di luar nilai yang harus diubah.
gsub!
.
update
menyampaikan maksud lebih baik daripada merge!
. Saya pikir ini adalah jawaban terbaik.
k
, gunakan _
saja.
Sedikit lebih mudah dibaca, map
ke array hash elemen tunggal dan reduce
denganmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(untuk map
) lalu a Hash
. Kemudian, setiap langkah operasi pengurangan akan menduplikasi "memo" Hash
dan menambahkan pasangan nilai kunci yang baru ke dalamnya. Setidaknya digunakan :merge!
dalam reduce
memodifikasi final Hash
di tempat. Dan pada akhirnya, Anda tidak mengubah nilai-nilai objek yang ada tetapi membuat objek baru, yang bukan pertanyaannya.
nil
jika the_hash
kosong
Ada metode 'Rails way' baru untuk tugas ini :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
. Ini harus menjadi cara untuk pergi dari sekarang.
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.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]
Hash.map
semantik berubah di Ruby 2.x?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
di Ruby 1.9 (IIRC) yang menghindari perlu mengakses nama secara langsung dan Map#merge
mungkin juga berfungsi. Tidak yakin bagaimana detail rumitnya berbeda.
Setelah mengujinya dengan RSpec seperti ini:
describe Hash do
describe :map_values do
it 'should map the values' do
expect({:a => 2, :b => 3}.map_values { |x| x ** 2 }).to eq({:a => 4, :b => 9})
end
end
end
Anda dapat menerapkan Hash # map_values sebagai berikut:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
Fungsi kemudian dapat digunakan seperti ini:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Jika Anda penasaran varian inplace mana yang tercepat di sini adalah:
Calculating -------------------------------------
inplace transform_values! 1.265k (± 0.7%) i/s - 6.426k in 5.080305s
inplace update 1.300k (± 2.7%) i/s - 6.579k in 5.065925s
inplace map reduce 281.367 (± 1.1%) i/s - 1.431k in 5.086477s
inplace merge! 1.305k (± 0.4%) i/s - 6.630k in 5.080751s
inplace each 1.073k (± 0.7%) i/s - 5.457k in 5.084044s
inplace inject 697.178 (± 0.9%) i/s - 3.519k in 5.047857s