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?

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}

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)

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"}

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"}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.