web-dev-qa-db-fra.com

Comment changer les valeurs de hachage?

Je voudrais remplacer chaque value dans un hachage avec value.some_method.

Par exemple, pour un hachage simple:

{"a" => "b", "c" => "d"}` 

chaque valeur doit être .upcased, donc elle ressemble à:

{"a" => "B", "c" => "D"}

J'ai essayé #collect et #map mais toujours récupérer les tableaux. Y a-t-il une manière élégante de faire ceci?

METTRE À JOUR 

Bon sang, j'ai oublié: le hash est dans une variable d'instance qui ne devrait pas être changée. J'ai besoin d'un nouveau hachage avec les valeurs modifiées, mais je préférerais ne pas définir cette variable explicitement, puis passer en boucle sur le hachage qui la remplit. Quelque chose comme:

new_hash = hash.magic{ ... }
110
Adam Nonymous
my_hash.each { |k, v| my_hash[k] = v.upcase } 

ou, si vous préférez le faire de manière non destructive, et retourner un nouveau hash au lieu de modifier my_hash:

a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h } 

Cette dernière version présente l’avantage supplémentaire de pouvoir également transformer les clés.

201
kch

Depuis Ruby 2.4.0, vous pouvez utiliser la méthode native Hash#transform_values :

hash = {"a" => "b", "c" => "d"}
new_hash = hash.transform_values(&:upcase)
# => {"a" => "B", "c" => "D"}

Il existe également une version destructive Hash#transform_values! .

66
potashin

Vous pouvez collecter les valeurs et les convertir à nouveau de tableau en hachage.

Comme ça:

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

Cela va le faire:

my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }

Contrairement à inject, l'avantage est que vous n'avez pas besoin de retourner le hachage à l'intérieur du bloc.

21
Konrad Reiche

Essayez cette fonction:

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

Il existe une méthode pour cela dans ActiveSupport v4.2.0. Il s'appelle transform_values et exécute simplement un bloc pour chaque paire clé-valeur.

Puisqu'ils le font avec une each, je pense qu'il n'y a pas de meilleur moyen que de passer en boucle.

hash = {sample: 'gach'}

result = {}
hash.each do |key, value|
  result[key] = do_stuff(value)
end

Mettre à jour:

Depuis Ruby 2.4.0, vous pouvez utiliser nativement #transform_values et #transform_values! .

9
schmijos

Je fais quelque chose comme ça:

new_hash = Hash [* original_hash.collect {| clé, valeur | [clé, valeur + 1]}. aplatir]

Cela vous donne les moyens de transformer la clé ou la valeur via n'importe quelle expression également (et c'est non destructif, bien sûr).

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

Vous voudrez peut-être aller plus loin et le faire sur un hachage imbriqué. Cela se produit certainement beaucoup avec les projets Rails. 

Voici un code pour vous assurer qu'un hachage params est en UTF-8:

  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 a la méthode tap ( 1.8.7 , 1.9.3 et 2.1.0 ) qui est très utile pour ce genre de choses.

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

Si vous savez que les valeurs sont des chaînes, vous pouvez appeler la méthode replace pendant le cycle: vous pourrez ainsi modifier la valeur.

En ce qui concerne Altohugh, cela se limite au cas dans lequel les valeurs sont des chaînes et ne répond donc pas complètement à la question.

1
reallynice

Spécifique aux rails

Si quelqu'un n'a qu'à appeler la méthode to_s à chacune des valeurs et n'utilise pas Rails 4.2 (qui inclut la méthode transform_valueslink ), vous pouvez procéder comme suit:

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"}

Remarque: L'utilisation de la bibliothèque 'json' est requise.

Note 2: Cela transformera également les clés en chaînes

0
lllllll