Bitte klären Sie, ob Sie die ursprünglichen Zeichenfolgenobjekte mutieren möchten, nur das Original mutieren möchten oder nichts mutieren möchten.
Phrogz
1
Dann haben Sie die falsche Antwort akzeptiert. (Keine Beleidigung für @pst beabsichtigt, da ich persönlich auch eine funktionale Programmierung befürworte, anstatt Objekte zu mutieren.)
Phrogz
Aber trotzdem war der Ansatz soo nett
theReverseFlick
Antworten:
178
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 }
@ Andrew Marshall Richtig, danke. Hash.[]Akzeptiert in Ruby 1.8 kein Array von Array-Paaren, erfordert es eine gerade Anzahl direkter Argumente (daher der Splat im Voraus).
Phrogz
2
Tatsächlich wurde Hash. [Key_value_pairs] in 1.8.7 eingeführt, sodass nur Ruby 1.8.6 das Splat & Flatten nicht benötigt.
Marc-André Lafortune
2
@Aupajo Hash#eachgibt 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.
Phrogz
1
Ich denke du meinst my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, muss den Hash aus dem Block zurückgeben
aceofspades
1
Alternativ können Sie auch die Methode each_value verwenden, die etwas einfacher zu verstehen ist als die Verwendung eines Unterstrichs für den nicht verwendeten Schlüsselwert.
Danke, war eigentlich das, wonach ich gesucht habe. Ich weiß nicht, warum Sie nicht so gut bewertet sind.
Simperreault
7
Dies ist jedoch sehr langsam und sehr RAM-hungrig. Der Eingabe-Hash wird wiederholt, um einen Zwischensatz verschachtelter Arrays zu erzeugen, die dann in einen neuen Hash konvertiert werden. Wenn Sie die RAM-Spitzenauslastung ignorieren, ist die Laufzeit viel schlechter. Wenn Sie dies in einer anderen Antwort mit den Modify-in-Place-Lösungen vergleichen, werden 2,5 Sekunden gegenüber 1,5 Sekunden über dieselbe Anzahl von Iterationen angezeigt. Da Ruby eine vergleichsweise langsame Sprache ist, ist es sehr sinnvoll, langsame Teile der langsamen Sprache zu vermeiden :-)
Andrew Hodgkinson
2
@AndrewHodgkinson, obwohl ich im Allgemeinen zustimme und nicht befürworte, nicht auf die Laufzeitleistung zu achten, wird das Verfolgen all dieser Leistungsprobleme nicht zu einem Schmerz und widerspricht der "Entwicklerproduktivität zuerst" -Philosophie von Ruby? Ich denke, dies ist weniger ein Kommentar für Sie als vielmehr ein allgemeiner Kommentar zu dem möglichen Paradoxon, zu dem wir mit Ruby kommen.
Elsurudo
4
Das Rätsel ist: Nun, wir geben bereits die Leistung bei unserer Entscheidung auf, sogar Rubin zu verwenden. Welchen Unterschied macht also "dieses andere kleine bisschen"? Es ist ein rutschiger Hang, nicht wahr? Aus Gründen der Lesbarkeit ziehe ich diese Lösung der akzeptierten Antwort vor.
Elsurudo
1
Wenn Sie Ruby 2.4+ verwenden, ist die Verwendung noch einfacher, #transform_values!wie von sschmeck ( stackoverflow.com/a/41508214/6451879 ) hervorgehoben.
Nicht genau richtig: Neue Zeichenfolgen werden zugewiesen. Trotzdem eine interessante Lösung, die effektiv ist. +1
Phrogz
@Phrogz guter Punkt; Ich habe die Antwort aktualisiert. Die Wertzuweisung kann im Allgemeinen nicht vermieden werden, da nicht alle Werttransformationen als Mutatoren wie z gsub!.
Sim
3
Wie meine Antwort, aber mit einem anderen Synonym stimme ich zu, dass updatedie Absicht besser vermittelt als merge!. Ich denke, das ist die beste Antwort.
1
Wenn Sie nicht verwenden k, verwenden Sie _stattdessen.
sekrett
28
Ein bisschen besser lesbar, mapes zu einem Array von Einzelelement-Hashes und reducedas mitmerge
Klarer zu bedienenHash[the_hash.map { |key,value| [key, "%#{value}%"] }]
meagar
Dies ist eine äußerst ineffiziente Methode zum Aktualisieren von Werten. Für jedes Wertepaar wird zuerst ein Paar Array(für map) und dann a erstellt Hash. In jedem Schritt der Reduzierungsoperation wird dann das "Memo" dupliziert Hashund 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.
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.mapkeine 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.mapSemantik in Ruby 2.x ändert]
Ich mag keine Nebenwirkungen, aber +1 für den Ansatz :) Es gibt each_with_objectin Ruby 1.9 (IIRC), das den direkten Zugriff auf den Namen vermeidet und Map#mergemöglicherweise auch funktioniert. Ich bin mir nicht sicher, wie sich die komplizierten Details unterscheiden.
1
Der anfängliche Hash wird geändert - dies ist in Ordnung, wenn das Verhalten erwartet wird, kann jedoch subtile Probleme verursachen, wenn es "vergessen" wird. Ich ziehe es vor, die Veränderlichkeit von Objekten zu reduzieren, aber es ist möglicherweise nicht immer praktisch. (Ruby ist kaum eine "
8
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%"}
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):
Wenn Sie möchten, dass sich der Hash ändert, die Zeichenfolgen jedoch nicht beeinflusst werden (Sie möchten, dass neue Zeichenfolgen angezeigt werden):
Wenn Sie einen neuen Hash möchten:
quelle
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ückgebenIn Ruby 2.1 und höher können Sie dies tun
quelle
#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.quelle
Hash#transform_values
(ohne den Knall), der den Empfänger nicht verändert. Ansonsten eine tolle Antwort, danke!reduce
Der beste Weg, um die vorhandenen Hash-Werte zu ändern, ist
Weniger Code und klare Absichten. Auch schneller, da über die zu ändernden Werte hinaus keine neuen Objekte zugewiesen werden.
quelle
gsub!
.update
die Absicht besser vermittelt alsmerge!
. Ich denke, das ist die beste Antwort.k
, verwenden Sie_
stattdessen.Ein bisschen besser lesbar,
map
es zu einem Array von Einzelelement-Hashes undreduce
das mitmerge
quelle
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(fürmap
) und dann a erstelltHash
. In jedem Schritt der Reduzierungsoperation wird dann das "Memo" dupliziertHash
und das neue Schlüssel-Wert-Paar hinzugefügt. Verwenden Sie mindestens:merge!
inreduce
, um das endgültige Finale zu ändernHash
. Und am Ende ändern Sie nicht die Werte des vorhandenen Objekts, sondern erstellen ein neues Objekt, was in der Frage nicht gestellt wurde.nil
wennthe_hash
es leer istFür diese Aufgabe gibt es eine neue 'Rails Way'-Methode :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
quelle
Hash#transform_values
. Dies sollte von nun an der richtige Weg sein.Eine Methode, die dem Original keine Nebenwirkungen verleiht:
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]quelle
Hash.map
Semantik in Ruby 2.x ändert?quelle
each_with_object
in Ruby 1.9 (IIRC), das den direkten Zugriff auf den Namen vermeidet undMap#merge
möglicherweise auch funktioniert. Ich bin mir nicht sicher, wie sich die komplizierten Details unterscheiden.Hash.merge! ist die sauberste Lösung
quelle
Nach dem Testen mit RSpec wie folgt:
Sie können Hash # map_values wie folgt implementieren:
Die Funktion kann dann folgendermaßen verwendet werden:
quelle
Wenn Sie neugierig sind, welche Inplace-Variante hier die schnellste ist, ist sie:
quelle