Rails mapeando matriz de hashes em um único hash

91

Eu tenho uma matriz de hashes assim:

 [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

E estou tentando mapear isso em um único hash assim:

{"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Eu consegui usando

  par={}
  mitem["params"].each { |h| h.each {|k,v| par[k]=v} } 

Mas eu queria saber se é possível fazer isso de uma forma mais idiomática (de preferência sem usar uma variável local).

Como posso fazer isso?

Bart Platak
fonte

Respostas:

160

Você pode compor Enumerable#reducee Hash#mergerealizar o que quiser.

input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
  is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Reduzir um array é como colocar uma chamada de método entre cada elemento dele.

Por exemplo, [1, 2, 3].reduce(0, :+)é como dizer 0 + 1 + 2 + 3e dar 6.

No nosso caso fazemos algo semelhante, mas com a função merge, que mescla dois hashes.

[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
  is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
  is {:a => 1, :b => 2, :c => 3}
cjhveal
fonte
1
Obrigado, esta é uma ótima resposta :) Muito bem explicado!
Bart Platak
41
input.reduce (&: merge) é suficiente.
redgetan
@redgetan isso é diferente de input.reduce(:merge)?
David van Geest
1
@David van Geest: Nesse caso, eles são equivalentes. O E comercial unário, conforme usado aqui, cria um bloco a partir do símbolo. No entanto, reduza tem um caso especial que aceita um símbolo. Eu queria evitar o operador E comercial unário para simplificar o exemplo, mas redgetan está correto ao dizer que o valor inicial é opcional neste caso.
cjhveal
1
Observe que se você usar em merge!vez mergedisso, o primeiro hash será modificado (o que você pode não querer), mas não criará um hash intermediário para cada nova mesclagem.
Phrogz
50

E se:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)
Shigeya
fonte
Este esquema é efetivamente igual ao que Joshua respondeu, mas aplicando repetidamente #merge (nome do método passado como um símbolo) em todos os hashes (pense em injetar como injetar um operador entre os itens). Consulte #inject .
shigeya
2
Por que não precisamos do E comercial, como em h.inject (&: merge)?
Donato
5
Porque o método inject aceita um símbolo como parâmetro a ser interpretado também como nome do método. É o recurso do injetar.
shigeya
9

Use #inject

hashes = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
merged = hashes.inject({}) { |aggregate, hash| aggregate.merge hash }
merged # => {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Joshua Cheek
fonte
0

Aqui você pode usar injetar ou reduzir da classe Enumerable, já que ambos são apelidos um do outro, portanto, não há benefício de desempenho para nenhum deles.

 sample = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

 result1 = sample.reduce(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

 result2 = sample.inject(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Nikhil Mohadikar
fonte