Je veux changer chaque valeur dans un hachage afin d'ajouter '%' avant et après la valeur afin
{ :a=>'a' , :b=>'b' }
doit être changé en
{ :a=>'%a%' , :b=>'%b%' }
Quelle est la meilleure façon de procéder?
Je veux changer chaque valeur dans un hachage afin d'ajouter '%' avant et après la valeur afin
{ :a=>'a' , :b=>'b' }
doit être changé en
{ :a=>'%a%' , :b=>'%b%' }
Quelle est la meilleure façon de procéder?
Réponses:
Si vous voulez que les chaînes réelles elles-mêmes muter sur place (affectant éventuellement et de manière souhaitable d'autres références aux mêmes objets chaîne):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }
Si vous voulez que le hachage change en place, mais que vous ne voulez pas affecter les chaînes (vous voulez qu'il obtienne de nouvelles chaînes):
# 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 }
Si vous voulez un nouveau hachage:
# 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.[]
n'accepte pas un tableau de paires de tableaux, il nécessite un nombre pair d'arguments directs (d'où le splat à l'avant).
Hash#each
la clé et la valeur au bloc. Dans ce cas, je me fichais de la clé et je ne lui ai donc rien donné d'utile. Les noms de variables peuvent commencer par un trait de soulignement et peuvent en fait être simplement un trait de soulignement. Il n'y a aucun avantage en termes de performances à faire cela, c'est juste une note subtile d'auto-documentation indiquant que je ne fais rien avec cette première valeur de bloc.
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, devez retourner le hachage du bloc
Dans Ruby 2.1 et supérieur, vous pouvez faire
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
comme indiqué par sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Ruby 2.4 a introduit la méthode Hash#transform_values!
que vous pouvez utiliser.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(sans le bang), qui ne modifie pas le récepteur. Sinon une excellente réponse, merci!
La meilleure façon de modifier les valeurs d'un Hash en place est
hash.update(hash){ |_,v| "%#{v}%" }
Moins de code et une intention claire. Aussi plus rapide car aucun nouvel objet n'est alloué au-delà des valeurs qui doivent être modifiées.
gsub!
.
update
exprime mieux l'intention que merge!
. Je pense que c'est la meilleure réponse.
k
, utilisez à la _
place.
Un peu plus lisible, map
c'est à un tableau de hachages à un seul élément et reduce
cela avecmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(pour map
) puis un Hash
. Ensuite, chaque étape de l'opération de réduction dupliquera le "mémo" Hash
et y ajoutera la nouvelle paire clé-valeur. Au moins une utilisation :merge!
dans reduce
de modifier la finale Hash
en place. Et au final, vous ne modifiez pas les valeurs de l'objet existant mais créez un nouvel objet, ce qui n'est pas ce que la question a posée.
nil
si the_hash
est vide
Il existe une nouvelle méthode 'Rails way' pour cette tâche :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
. Cela devrait être la voie à suivre à partir de maintenant.
Une méthode qui n'introduit pas d'effets secondaires sur l'original:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
Hash # map peut également être une lecture intéressante car elle explique pourquoi le Hash.map
ne renvoie pas de Hash (c'est pourquoi le tableau de [key,value]
paires résultant est converti en un nouveau Hash) et fournit des approches alternatives au même modèle général.
Bon codage.
[Clause de non-responsabilité: je ne sais pas si la Hash.map
sémantique change dans Ruby 2.x]
Hash.map
sémantique change dans Ruby 2.x?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
dans Ruby 1.9 (IIRC) qui évite d'avoir à accéder directement au nom et Map#merge
peut aussi fonctionner. Je ne sais pas en quoi les détails complexes diffèrent.
Hash.merge! est la solution la plus propre
o = { a: 'a', b: 'b' }
o.merge!(o) { |key, value| "%#{ value }%" }
puts o.inspect
> { :a => "%a%", :b => "%b%" }
Après l'avoir testé avec RSpec comme ceci:
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
Vous pouvez implémenter Hash # map_values comme suit:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
La fonction peut alors être utilisée comme ceci:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Si vous êtes curieux de savoir quelle variante en place est la plus rapide, voici:
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