web-dev-qa-db-fra.com

Changer chaque valeur d'un hachage dans Ruby

Je veux changer chaque valeur d'un hachage afin d'ajouter '%' avant et après la valeur,

{ :a=>'a' , :b=>'b' }

doit être changé pour

{ :a=>'%a%' , :b=>'%b%' }

Quelle est la meilleure façon de faire cela?

153
theReverseFlick

Si vous voulez que les chaînes elles-mêmes mutent sur place (affectant éventuellement 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 à la 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 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}%"] } ]
165
Phrogz

Dans Ruby 2.1 et supérieur, vous pouvez faire

{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
247
shock_one

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%"} 
_
91
sschmeck

Le meilleur moyen de modifier les valeurs d'un hachage est

hash.update(hash){ |_,v| "%#{v}%" }

Moins de code et une intention claire. Également plus rapide car aucun nouvel objet n'est alloué au-delà des valeurs qui doivent être changées.

80
Sim

Un peu plus lisible, map il à un tableau de hachages à un seul élément et reduce qu'avec merge

the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
28
edgerunner

Il existe une nouvelle méthode 'Rails way' pour cette tâche :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values

17
woto

Une méthode qui n'introduit pas d'effets secondaires dans 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 hachage (ce qui explique pourquoi le tableau résultant de paires [key,value] est converti en un nouveau hachage) et propose des approches alternatives pour le même schéma général.

Bonne codage.

[Avertissement: je ne suis pas sûr si Hash.map la sémantique change dans Ruby 2.x]

16
user166390
my_hash.each do |key, value|
  my_hash[key] = "%#{value}%"
end
15
Andrew Marshall

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

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%"}
5
wedesoft