Ich möchte jede value
in einem Hash durch value.some_method
ersetzen.
Zum Beispiel für einen einfachen Hash:
{"a" => "b", "c" => "d"}`
jeder Wert sollte .upcase
d sein, also sieht es so aus:
{"a" => "B", "c" => "D"}
Ich habe #collect
und #map
ausprobiert, bekomme aber immer nur Arrays zurück. Gibt es eine elegante Möglichkeit, dies zu tun?
UPDATE
Verdammt, ich habe es vergessen: Der Hash ist in einer Instanzvariablen, die nicht geändert werden sollte. Ich brauche einen neuen Hash mit den geänderten Werten, würde es aber vorziehen, diese Variable nicht explizit zu definieren und dann den Hash zu füllen, der sie füllt. So etwas wie:
new_hash = hash.magic{ ... }
my_hash.each { |k, v| my_hash[k] = v.upcase }
oder, wenn Sie es vorziehen, es nicht destruktiv auszuführen, und einen neuen Hash zurückgeben, anstatt my_hash
zu ändern:
a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h }
Diese letzte Version hat den zusätzlichen Vorteil, dass Sie auch die Schlüssel umwandeln können.
Seit Ruby 2.4.0
können Sie die native Hash#transform_values
Methode verwenden:
hash = {"a" => "b", "c" => "d"}
new_hash = hash.transform_values(&:upcase)
# => {"a" => "B", "c" => "D"}
Es gibt auch eine destruktive Hash#transform_values!
version.
Sie können die Werte sammeln und erneut von Array in Hash konvertieren.
So was:
config = Hash[ config.collect {|k,v| [k, v.upcase] } ]
Das wird es tun:
my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }
Im Gegensatz zu inject
besteht der Vorteil darin, dass Sie den Hash nicht erneut innerhalb des Blocks zurückgeben müssen.
Versuchen Sie diese Funktion:
h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.
In ActiveSupport v4.2.0 gibt es dafür eine Methode. Es heißt transform_values
und führt im Prinzip nur einen Block für jedes Schlüsselwertpaar aus.
Da sie es mit einer each
machen, denke ich, gibt es keinen besseren Weg, als durchzugehen.
hash = {sample: 'gach'}
result = {}
hash.each do |key, value|
result[key] = do_stuff(value)
end
Update:
Seit Ruby 2.4.0 können Sie #transform_values
und #transform_values!
nativ verwenden.
Ich mache so etwas:
new_hash = Hash [* original_hash.collect {| key, value | [Schlüssel, Wert + 1]}. Abflachen]
Dadurch haben Sie die Möglichkeit, den Schlüssel oder Wert auch über einen beliebigen Ausdruck zu transformieren (und das ist natürlich nicht destruktiv).
new_hash = old_hash.merge(old_hash) do |_key, value, _value|
value.upcase
end
# old_hash = {"a" => "b", "c" => "d"}
# new_hash = {"a" => "B", "c" => "D"}
Möglicherweise möchten Sie einen Schritt weiter gehen und dies bei einem verschachtelten Hash tun. Bei Rails-Projekten ist dies sicherlich ein ziemlicher Betrag.
Hier ist etwas Code, um sicherzustellen, dass ein Params-Hash in UTF-8 enthalten ist:
def convert_hash hash
hash.inject({}) do |h,(k,v)|
if v.kind_of? String
h[k] = to_utf8(v)
else
h[k] = convert_hash(v)
end
h
end
end
# Iconv UTF-8 helper
# Converts strings into valid UTF-8
#
# @param [String] untrusted_string the string to convert to UTF-8
# @return [String] your string in UTF-8
def to_utf8 untrusted_string=""
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
ic.iconv(untrusted_string + ' ')[0..-2]
end
Wenn Sie wissen, dass es sich bei den Werten um Strings handelt, können Sie die replace - Methode im Zyklus aufrufen: Auf diese Weise ändern Sie den Wert.
Alles in allem ist dies auf den Fall beschränkt, in dem die Werte Strings sind und daher die Frage nicht vollständig beantwortet. Ich dachte, es kann für jemanden nützlich sein.
Rails-spezifisch
Falls jemand nur die to_s
-Methode für jeden der Werte aufrufen muss und nicht Rails 4.2 (zu dem die transform_values
-Methode link gehört) verwendet, können Sie Folgendes tun:
original_hash = { :a => 'a', :b => BigDecimal('23.4') }
#=> {:a=>"a", :b=>#<BigDecimal:5c03a00,'0.234E2',18(18)>}
JSON(original_hash.to_json)
#=> {"a"=>"a", "b"=>"23.4"}
Hinweis: Die Verwendung der 'json'-Bibliothek ist erforderlich.
Hinweis 2: Dies macht aus Schlüsseln auch Strings