Bir değerdeki her değeri, değerden önce ve sonra '%' eklemek için değiştirmek istiyorum
{ :a=>'a' , :b=>'b' }
olarak değiştirilmeli
{ :a=>'%a%' , :b=>'%b%' }
Bunu yapmanın en iyi yolu nedir?
Bir değerdeki her değeri, değerden önce ve sonra '%' eklemek için değiştirmek istiyorum
{ :a=>'a' , :b=>'b' }
olarak değiştirilmeli
{ :a=>'%a%' , :b=>'%b%' }
Bunu yapmanın en iyi yolu nedir?
Yanıtlar:
Gerçek dizelerin kendilerinin yerinde değişmesini istiyorsanız (muhtemelen ve istenen şekilde aynı dize nesnelerine yapılan diğer başvuruları etkiler):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }
Karma'nın yerinde değişmesini istiyorsanız, ancak dizeleri etkilemek istemiyorsanız (yeni dizeler almasını istiyorsanız):
# 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 }
Yeni bir karma istiyorsanız:
# 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.[]bir dizi dizi çiftini kabul etmez , çift sayıda doğrudan argüman gerektirir (bu nedenle önden uyarı).
Hash#eachhem anahtar hem de değer verir. Bu durumda, anahtarı umursamadım ve bu yüzden yararlı bir şey söylemedim. Değişken isimleri bir alt çizgi ile başlayabilir ve aslında sadece bir alt çizgi olabilir. Bunu yapmanın hiçbir performans avantajı yok, bu ilk blok değeri ile hiçbir şey yapmıyorum sadece kendini belgeleyen ince bir not.
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, hash bloktan geri dönmek zorunda
Ruby 2.1 ve üstü sürümlerde şunları yapabilirsiniz:
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!2.4+ kullanıyorsanız , sschmeck ( stackoverflow.com/a/41508214/6451879 ) tarafından belirtildiği gibi kullanmak daha da kolaydır .
Ruby 2.4 Hash#transform_values!kullanabileceğiniz yöntemi tanıttı .
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_valuesalıcıyı değiştirmeyen (patlama olmadan) da var. Aksi takdirde harika bir cevap, teşekkürler!
Bir Hash'in değerlerini yerinde değiştirmenin en iyi yolu
hash.update(hash){ |_,v| "%#{v}%" }
Daha az kod ve açık niyet. Ayrıca, değiştirilmesi gereken değerlerin ötesinde hiçbir yeni nesne tahsis edilmediğinden daha hızlıdır.
gsub!.
updateniyeti daha iyi ilettiğini kabul ediyorum merge!. Bence bu en iyi cevap.
k, kullanmak _yerine.
A, daha okunabilir bir bit mapbu tek öğeli karma bir dizi ve reduceomerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array(for map) sonra a oluşturur Hash. Daha sonra, azaltma işleminin her adımı "not" u çoğaltacak Hashve yeni anahtar / değer çiftini buna ekleyecektir. En kullanımda az :merge!yer reducenihai değiştirmek için Hashyerinde. Ve sonunda, mevcut nesnenin değerlerini değiştirmiyorsunuz, ancak sorunun sorduğu yeni bir nesne oluşturuyorsunuz.
nileğer the_hashboş
Bu görev için yeni bir 'Raylar yolu' yöntemi var :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values. Bundan böyle devam etmeli.
Orijinaline yan etkiler getirmeyen bir yöntem:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
Hash # haritası ayrıca Hash.mapbir Hash döndürmediğini açıklayan ilginç bir okuma olabilir (bu nedenle sonuçta ortaya çıkan [key,value]çiftler dizisi yeni bir Hash'e dönüştürülür) ve aynı genel desene alternatif yaklaşımlar sağlar.
Mutlu kodlama.
[Feragatname: Hash.mapRuby 2.x'te anlambilimin değişip değişmediğinden emin değilim ]
Hash.map, Ruby 2.x'te anlambilimin değişip değişmediğini bile biliyor mu?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_objectYakında 1.9 (IIRC) var, bu da isme doğrudan erişmeye gerek kalmıyor ve Map#mergeaynı zamanda işe yarayabilir. Karmaşık ayrıntıların nasıl farklı olduğundan emin değilim.
Bunun gibi RSpec ile test ettikten sonra:
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
Hash # map_values uygulamasını aşağıdaki gibi uygulayabilirsiniz:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
Fonksiyon daha sonra şu şekilde kullanılabilir:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Hangi en hızlı varyantın burada en hızlı olduğunu merak ediyorsanız:
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