Rails mappatura array di hash su un singolo hash


92

Ho una serie di hash in questo modo:

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

E sto cercando di mappare questo su un singolo hash in questo modo:

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

L'ho raggiunto usando

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

Ma mi chiedevo se fosse possibile farlo in un modo più idiomatico (preferibilmente senza usare una variabile locale).

Come posso fare questo?

Risposte:


161

Potresti comporre Enumerable#reducee Hash#mergerealizzare quello che vuoi.

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

Ridurre un array è come incollare una chiamata al metodo tra ogni elemento di esso.

Ad esempio [1, 2, 3].reduce(0, :+)è come dire 0 + 1 + 2 + 3e dare6 .

Nel nostro caso facciamo qualcosa di simile, ma con la funzione merge, che unisce due hash.

[{: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
Grazie, questa è un'ottima risposta :) Molto ben spiegato!
Bart Platak

41
input.reduce (&: merge) è sufficiente.
redgetan

@redgetan è diverso da input.reduce(:merge)?
David van Geest

1
@David van Geest: in questo caso sono equivalenti. La e commerciale unaria usata qui crea un blocco dal simbolo. Tuttavia, reduce ha un caso speciale che accetta un simbolo. Volevo evitare l'operatore e commerciale unario per semplificare l'esempio, ma redgetan è corretto sul fatto che il valore iniziale è facoltativo in questo caso.
cjhveal

1
Nota che se lo usi al merge!posto di mergeesso modificherai il primo hash (che potresti non volere) ma non creerà un hash intermedio per ogni nuova unione.
Phrogz

50

Che ne dite di:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)

Questo schema è effettivamente lo stesso di quello che ha risposto Joshua, ma applica ripetutamente #merge (nome del metodo passato come simbolo) su tutti gli hash (pensa a inject come iniezione di un operatore tra gli elementi). Fare riferimento a #inject .
shigeya

2
Come mai non abbiamo bisogno della e commerciale, come in h.inject (&: merge)?
Donato

5
Perché il metodo inject accetta un simbolo come parametro da interpretare anche come nome del metodo. È la caratteristica di Inject.
shigeya

9

Usa #inject

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

0

Qui puoi usare inject o reduce dalla classe Enumerable poiché entrambi sono alias l'uno dell'altro, quindi non c'è alcun vantaggio in termini di prestazioni per nessuno dei due.

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

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

 result2 = sample.inject(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.