Raíles que mapean una matriz de hashes en un solo hash

92

Tengo una variedad de hashes como este:

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

Y estoy tratando de mapear esto en un solo hash como este:

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

Lo he logrado usando

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

Pero me preguntaba si es posible hacer esto de una manera más idiomática (preferiblemente sin usar una variable local).

¿Cómo puedo hacer esto?

Bart Platak
fuente

Respuestas:

161

Podrías componer Enumerable#reducey Hash#mergelograr lo que quieras.

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

Reducir una matriz es como pegar una llamada a un método entre cada elemento de la misma.

Por ejemplo [1, 2, 3].reduce(0, :+)es como decir 0 + 1 + 2 + 3y dar 6.

En nuestro caso hacemos algo similar, pero con la función merge, que fusiona dos 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
fuente
1
Gracias, esta es una gran respuesta :) ¡Muy bien explicado!
Bart Platak
41
input.reduce (&: merge) es suficiente.
redgetan
@redgetan ¿eso es diferente de input.reduce(:merge)?
David van Geest
1
@David van Geest: En este caso son equivalentes. El ampersand unario, como se usa aquí, construye un bloque a partir del símbolo. Sin embargo, reduce tiene un caso especial que acepta un símbolo. Quería evitar el operador ampersand unario para simplificar el ejemplo, pero redgetan tiene razón en que el valor inicial es opcional en este caso.
cjhveal
1
Tenga en cuenta que si usa en merge!lugar de merge, modificará el primer hash (que es posible que no desee) pero no creará un hash intermedio para cada nueva combinación.
Phrogz
50

Qué tal si:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)
shigeya
fuente
Este esquema es efectivamente el mismo que respondió Joshua, pero aplicando repetidamente #merge (nombre del método pasado como símbolo) en todos los hashes (piense en inyectar como inyectar un operador entre elementos). Consulte #inject .
shigeya
2
¿Por qué no necesitamos el ampersand, como en h.inject (&: merge)?
Donato
5
Porque el método de inyección acepta un símbolo como parámetro para ser interpretado también como nombre de método. Es la función de inyección.
shigeya
9

Utilice #inject

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

Aquí puede usar inyectar o reducir de la clase Enumerable , ya que ambos son alias entre sí, por lo que no hay beneficio de rendimiento para ninguno.

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

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

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