wake-up-neo.net

Wie kann man Hashwerte ändern?

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 .upcased 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{ ... }
110
Adam Nonymous
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.

201
kch

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.

66
potashin

Sie können die Werte sammeln und erneut von Array in Hash konvertieren.

So was:

config = Hash[ config.collect {|k,v| [k, v.upcase] } ]
32
Endel

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.

21
Konrad Reiche

Versuchen Sie diese Funktion:

h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.
10
Chris Doggett

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.

9
schmijos

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).

1
Cory Chamblin
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"}
1
cnst

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  
1
Tokumine

Ruby hat die tap-Methode ( 1.8.7 , 1.9.3 und 2.1.0 ), die für solche Sachen sehr nützlich ist.

original_hash = { :a => 'a', :b => 'b' }
original_hash.clone.tap{ |h| h.each{ |k,v| h[k] = v.upcase } }
# => {:a=>"A", :b=>"B"}
original_hash # => {:a=>"a", :b=>"b"}
1
Nate

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.

1
reallynice

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

0
lllllll