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 .upcase
d, 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{ ... }
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.
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!
.
Vous pouvez collecter les valeurs et les convertir à nouveau de tableau en hachage.
Comme ça:
config = Hash[ config.collect {|k,v| [k, v.upcase] } ]
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.
Essayez cette fonction:
h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.
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!
.
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).
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"}
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
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.
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_values
link ), 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