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.