Voglio cambiare ogni valore in un hash in modo da aggiungere '%' prima e dopo il valore così
{ :a=>'a' , :b=>'b' }
deve essere modificato in
{ :a=>'%a%' , :b=>'%b%' }
Qual'è il miglior modo per farlo?
Voglio cambiare ogni valore in un hash in modo da aggiungere '%' prima e dopo il valore così
{ :a=>'a' , :b=>'b' }
deve essere modificato in
{ :a=>'%a%' , :b=>'%b%' }
Qual'è il miglior modo per farlo?
Risposte:
Se vuoi che le stringhe stesse mutino sul posto (possibilmente e desiderabilmente influenzano altri riferimenti agli stessi oggetti stringa):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }
Se vuoi che l'hash cambi sul posto, ma non vuoi influenzare le stringhe (vuoi che ottenga nuove stringhe):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |key,str| my_hash[key] = "%#{str}%" }
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
Se vuoi un nuovo hash:
# Ruby 1.8.6+
new_hash = Hash[*my_hash.map{|k,str| [k,"%#{str}%"] }.flatten]
# Ruby 1.8.7+
new_hash = Hash[my_hash.map{|k,str| [k,"%#{str}%"] } ]
Hash.[]
non accetta un array di coppie di array, richiede un numero pari di argomenti diretti (da qui il simbolo iniziale).
Hash#each
restituisce sia la chiave che il valore al blocco. In questo caso, non mi importava della chiave, e quindi non ho nominato nulla di utile. I nomi delle variabili possono iniziare con un trattino basso, e in effetti può essere solo un trattino basso. Non vi è alcun vantaggio in termini di prestazioni nel fare questo, è solo una sottile nota autocompattante che non sto facendo nulla con quel primo valore di blocco.
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, devi restituire l'hash dal blocco
In Ruby 2.1 e versioni successive puoi farlo
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
come fuori punte da sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Ruby 2.4 ha introdotto il metodo Hash#transform_values!
, che è possibile utilizzare.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(senza il botto), che non modifica il ricevitore. Altrimenti un'ottima risposta, grazie!
Il modo migliore per modificare i valori di un hash sul posto è
hash.update(hash){ |_,v| "%#{v}%" }
Meno codice e intenti chiari. Anche più veloce perché non vengono allocati nuovi oggetti oltre i valori che devono essere modificati.
gsub!
.
update
trasmetta l'intenzione meglio di merge!
. Penso che questa sia la risposta migliore.
k
, usa _
invece.
Un po 'più leggibile, map
per un array di hash a elemento singolo e reduce
quello conmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(per map
) quindi a Hash
. Quindi, ogni passaggio dell'operazione di riduzione duplicherà il "memo" Hash
e vi aggiungerà la nuova coppia chiave-valore. Almeno uso :merge!
nel reduce
modificare il finale Hash
in luogo. E alla fine, non stai modificando i valori dell'oggetto esistente ma stai creando un nuovo oggetto, che non è quello che la domanda ha posto.
nil
se the_hash
è vuoto
Esiste un nuovo metodo "Rails way" per questa attività :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
. Questo dovrebbe essere il modo di andare da ora in poi.
Un metodo che non introduce effetti collaterali nell'originale:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
La mappa Hash # può anche essere una lettura interessante in quanto spiega perché Hash.map
non restituisce un hash (motivo per cui la risultante matrice di[key,value]
coppie viene convertita in un nuovo hash) e fornisce approcci alternativi allo stesso schema generale.
Buona codifica.
[Dichiarazione di non responsabilità: non sono sicuro che la Hash.map
semantica cambi in Ruby 2.x]
Hash.map
semantica cambia in Ruby 2.x?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
in Ruby 1.9 (IIRC) che evita la necessità di accedere direttamente al nome e Map#merge
potrebbe anche funzionare. Non sono sicuro di come differiscano i dettagli complessi.
Dopo averlo testato con RSpec in questo modo:
describe Hash do
describe :map_values do
it 'should map the values' do
expect({:a => 2, :b => 3}.map_values { |x| x ** 2 }).to eq({:a => 4, :b => 9})
end
end
end
È possibile implementare Hash # map_values come segue:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
La funzione può quindi essere utilizzata in questo modo:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Se sei curioso di sapere quale variante inplace è la più veloce qui è:
Calculating -------------------------------------
inplace transform_values! 1.265k (± 0.7%) i/s - 6.426k in 5.080305s
inplace update 1.300k (± 2.7%) i/s - 6.579k in 5.065925s
inplace map reduce 281.367 (± 1.1%) i/s - 1.431k in 5.086477s
inplace merge! 1.305k (± 0.4%) i/s - 6.630k in 5.080751s
inplace each 1.073k (± 0.7%) i/s - 5.457k in 5.084044s
inplace inject 697.178 (± 0.9%) i/s - 3.519k in 5.047857s