ruby - rails - ¿Cómo cambiar los valores de Hash?
hash.map ruby (12)
Me gustaría reemplazar cada value
en un hash con value.some_method
.
Por ejemplo, dado un hash simple:
{"a" => "b", "c" => "d"}`
cada valor debe ser .upcase
d, por lo que se ve así:
{"a" => "B", "c" => "D"}
Intenté #collect
y #map
pero siempre #map
las matrices. ¿Hay una manera elegante de hacer esto?
ACTUALIZAR
Maldita sea, lo olvidé: el hash está en una variable de instancia que no debería cambiarse. Necesito un hash nuevo con los valores modificados, pero preferiría no definir esa variable explícitamente y luego pasar por encima del hash que la llena. Algo como:
new_hash = hash.magic{ ... }
Desde ruby 2.4.0
puede usar Hash#transform_values
método Hash#transform_values
nativo:
hash = {"a" => "b", "c" => "d"}
new_hash = hash.transform_values(&:upcase)
# => {"a" => "B", "c" => "D"}
¡También hay Hash#transform_values destructivos Hash#transform_values versión.
Es posible que desee ir un paso más allá y hacer esto en un hash anidado. Ciertamente, esto ocurre bastante con los proyectos de Rails.
Aquí hay un código para asegurarse de que hash de 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
Esto lo hará:
my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }
A diferencia de inject
la ventaja es que no necesita devolver el hash nuevamente dentro del bloque.
Hay un método para eso en ActiveSupport v4.2.0. Se llama transform_values
y básicamente solo ejecuta un bloque para cada par clave-valor-par.
Como lo están haciendo con each
, creo que no hay mejor manera de pasar.
hash = {sample: ''gach''}
result = {}
hash.each do |key, value|
result[key] = do_stuff(value)
end
Prueba esta función:
h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.
Puede recopilar los valores y convertirlos de Array a Hash nuevamente.
Me gusta esto:
config = Hash[ config.collect {|k,v| [k, v.upcase] } ]
Si sabe que los valores son cadenas, puede llamar al método de replace mientras está dentro del ciclo: de esta manera, cambiará el valor.
Altohugh esto se limita al caso en que los valores son cadenas y por lo tanto no responde la pregunta completamente, pensé que puede ser útil para alguien.
Yo hago algo como esto:
new_hash = Hash [* original_hash.collect {| key, value | [clave, valor + 1]}. aplanar]
Esto le proporciona las facilidades para transformar la clave o valor a través de cualquier expresión también (y no es destructivo, por supuesto).
Rails-specific
En caso de que alguien solo necesite llamar to_s
método to_s
para cada uno de los valores y no utilice Rails 4.2 (que incluye el link método transform_values
), puede hacer lo siguiente:
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"}
Nota: Se requiere el uso de la biblioteca ''json''.
Nota 2: Esto convertirá las claves en cadenas también
my_hash.each { |k, v| my_hash[k] = v.upcase }
o, si prefieres hacerlo de forma no destructiva, y devuelves un nuevo hash en lugar de modificar my_hash
:
a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h }
Esta última versión tiene el beneficio adicional de que también puede transformar las claves.
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"}