Ich möchte jeden Wert in einem Hash ändern, um '%' vor und nach dem Wert hinzuzufügen
{ :a=>'a' , :b=>'b' }
muss geändert werden auf
{ :a=>'%a%' , :b=>'%b%' }
Was ist der beste Weg, dies zu tun?
Ich möchte jeden Wert in einem Hash ändern, um '%' vor und nach dem Wert hinzuzufügen
{ :a=>'a' , :b=>'b' }
muss geändert werden auf
{ :a=>'%a%' , :b=>'%b%' }
Was ist der beste Weg, dies zu tun?
Antworten:
Wenn Sie möchten, dass die tatsächlichen Zeichenfolgen selbst an Ort und Stelle mutieren (was möglicherweise und wünschenswert andere Verweise auf dieselben Zeichenfolgenobjekte beeinflusst):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }
Wenn Sie möchten, dass sich der Hash ändert, die Zeichenfolgen jedoch nicht beeinflusst werden (Sie möchten, dass neue Zeichenfolgen angezeigt werden):
# 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 }
Wenn Sie einen neuen Hash möchten:
# 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.[]
Akzeptiert in Ruby 1.8 kein Array von Array-Paaren, erfordert es eine gerade Anzahl direkter Argumente (daher der Splat im Voraus).
Hash#each
gibt sowohl den Schlüssel als auch den Wert für den Block aus. In diesem Fall war mir der Schlüssel egal und ich nannte ihn nichts Nützliches. Variablennamen können mit einem Unterstrich beginnen und tatsächlich nur ein Unterstrich sein. Dies hat keinen Leistungsvorteil, es ist nur eine subtile selbstdokumentierende Notiz, dass ich mit diesem ersten Blockwert nichts mache.
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, muss den Hash aus dem Block zurückgeben
In Ruby 2.1 und höher können Sie dies tun
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
wie von sschmeck ( stackoverflow.com/a/41508214/6451879 ) hervorgehoben.
Ruby 2.4 führte die Methode ein Hash#transform_values!
, die Sie verwenden konnten.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(ohne den Knall), der den Empfänger nicht verändert. Ansonsten eine tolle Antwort, danke!
Der beste Weg, um die vorhandenen Hash-Werte zu ändern, ist
hash.update(hash){ |_,v| "%#{v}%" }
Weniger Code und klare Absichten. Auch schneller, da über die zu ändernden Werte hinaus keine neuen Objekte zugewiesen werden.
gsub!
.
update
die Absicht besser vermittelt als merge!
. Ich denke, das ist die beste Antwort.
k
, verwenden Sie _
stattdessen.
Ein bisschen besser lesbar, map
es zu einem Array von Einzelelement-Hashes und reduce
das mitmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(für map
) und dann a erstellt Hash
. In jedem Schritt der Reduzierungsoperation wird dann das "Memo" dupliziert Hash
und das neue Schlüssel-Wert-Paar hinzugefügt. Verwenden Sie mindestens :merge!
in reduce
, um das endgültige Finale zu ändern Hash
. Und am Ende ändern Sie nicht die Werte des vorhandenen Objekts, sondern erstellen ein neues Objekt, was in der Frage nicht gestellt wurde.
nil
wenn the_hash
es leer ist
Für diese Aufgabe gibt es eine neue 'Rails Way'-Methode :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
. Dies sollte von nun an der richtige Weg sein.
Eine Methode, die dem Original keine Nebenwirkungen verleiht:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
Die Hash # -Karte kann auch eine interessante Lektüre sein, da sie erklärt, warum die Hash.map
keine Hash zurückgibt (weshalb das resultierende Array von [key,value]
Paaren in eine neue Hash konvertiert wird) und alternative Ansätze für dasselbe allgemeine Muster bietet.
Viel Spaß beim Codieren.
[Haftungsausschluss: Ich bin nicht sicher, ob sich die Hash.map
Semantik in Ruby 2.x ändert]
Hash.map
Semantik in Ruby 2.x ändert?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
in Ruby 1.9 (IIRC), das den direkten Zugriff auf den Namen vermeidet und Map#merge
möglicherweise auch funktioniert. Ich bin mir nicht sicher, wie sich die komplizierten Details unterscheiden.
Hash.merge! ist die sauberste Lösung
o = { a: 'a', b: 'b' }
o.merge!(o) { |key, value| "%#{ value }%" }
puts o.inspect
> { :a => "%a%", :b => "%b%" }
Nach dem Testen mit RSpec wie folgt:
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
Sie können Hash # map_values wie folgt implementieren:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
Die Funktion kann dann folgendermaßen verwendet werden:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Wenn Sie neugierig sind, welche Inplace-Variante hier die schnellste ist, ist sie:
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