Como alterar os valores de hash?
Pergunta
Eu gostaria de substituir cada value
em um hash com value.some_method
.
Por exemplo, para um dado um hash simples:
{"a" => "b", "c" => "d"}`
cada valor deve ser .upcase
d, por isso se parece com:
{"a" => "B", "c" => "D"}
Eu tentei #collect
e #map
mas sempre apenas obter matrizes de volta. Existe uma maneira elegante de fazer isso?
Atualizar
Droga, eu esqueci: O hash é uma variável de instância que não deve ser alterado. Preciso de um novo hash com os valores alterados, mas prefere não definir essa variável explícita e, em seguida, um loop sobre o hash preenchê-lo. Algo como:
new_hash = hash.magic{ ... }
Solução
my_hash.each { |k, v| my_hash[k] = v.upcase }
ou, se você preferir fazê-lo de forma não destrutiva, e retornar um novo hash em vez de modificar my_hash
:
a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h }
Esta última versão tem a vantagem adicional de que você poderia transformar as chaves também.
Outras dicas
Desde ruby 2.4.0
você pode usar nativa Hash#transform_values
método:
hash = {"a" => "b", "c" => "d"}
new_hash = hash.transform_values(&:upcase)
# => {"a" => "B", "c" => "D"}
Há também destrutiva Hash#transform_values!
versão.
Você pode coletar os valores, e convertê-lo de Array para Hash novamente.
Como esta:
config = Hash[ config.collect {|k,v| [k, v.upcase] } ]
Isso vai fazê-lo:
my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }
Ao contrário de inject
a vantagem é que você não está em necessidade de retornar o hash novamente dentro do bloco.
Tente esta função:
h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.
Há um método para que em ActiveSupport v4.2.0. É chamado transform_values
e, basicamente, apenas executa um bloco para cada valor de par de chaves.
Uma vez que eles estão fazendo isso com um each
Eu acho que não há nenhuma maneira melhor do que para percorrer.
hash = {sample: 'gach'}
result = {}
hash.each do |key, value|
result[key] = do_stuff(value)
end
Update:
Desde o Ruby 2.4.0 você pode nativamente usar #transform_values
e #transform_values!
.
eu faço algo como isto:
new_hash = Hash [* original_hash.collect {| chave, valor | [Chave, valor + 1]}. Achatar]
Isso fornece as instalações para transformar a chave ou valor através de qualquer expressão também (e é não-destrutivo, é claro).
Você pode querer dar um passo adiante e fazer isso em um hash aninhado. Certamente isso acontece uma quantidade justa com projetos Rails.
Aqui está um código para garantir uma params hash é em 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
Ruby tem o método tap
( 1.8.7 , 1.9.3 e < a href = "http://ruby-doc.org/core-2.1.0/Object.html#method-i-tap" rel = "nofollow"> 2.1.0 ) que é muito útil para coisas como isso.
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"}
Se você sabe que os valores são strings, você pode chamar o substituir método sobre eles, enquanto no ciclo: desta forma você vai alterar o valor
.Altohugh esta se limita ao caso em que os valores são strings e, portanto, não responde a pergunta totalmente, eu pensei que pode ser útil para alguém.
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"}
Rails específica de
No caso de alguém só precisa chamar o método to_s
para cada um dos valores e não está usando Rails 4.2 (que inclui método transform_values
link ), você pode fazer o seguinte:
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: O uso de biblioteca 'json' é necessária
Nota 2: Isto irá transformar chaves em strings bem