web-dev-qa-db-ja.com

Rubyのハッシュのすべての値を変更する

ハッシュのすべての値を変更して、値の前後に「%」を追加したいので、

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

に変更する必要があります

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

これを行う最良の方法は何ですか?

151
theReverseFlick

実際の文字列自体をその場で変更したい場合(同じ文字列オブジェクトへの他の参照に影響を与える可能性があり、望ましくは):

# Two ways to achieve the same result (any Ruby version)
my_hash.each{ |_,str| str.gsub! /^|$/, '%' }
my_hash.each{ |_,str| str.replace "%#{str}%" }

ハッシュをその場で変更したいが、文字列に影響を与えたくない場合(新しい文字列を取得したい場合):

# 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 }

新しいハッシュが必要な場合:

# 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

Ruby 2.1以降では、次のことができます

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

Ruby 2.4では、メソッド Hash#transform_values! を導入しました。これは使用できます。

{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"} 
89
sschmeck

ハッシュの値を適切に変更する最良の方法は

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

より少ないコードと明確な意図。また、変更する必要がある値を超える新しいオブジェクトが割り当てられないため、高速です。

80
Sim

もう少し読みやすいもの、mapは単一要素ハッシュの配列に、reducemerge

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

このタスクのための新しい「レールウェイ」メソッドがあります:) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values

17
woto

オリジナルに副作用をもたらさない1つの方法:

h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]

Hash#map は、Hash.mapがハッシュを返さない理由([key,value]ペアの結果の配列が新しいハッシュに変換される理由)を説明し、同じ一般的なパターン。

ハッピーコーディング。

[免責事項:Hash.mapのセマンティクスがRuby 2.xで変更されるかどうかわかりません

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

Hash.merge!は最もクリーンなソリューションです

o = { a: 'a', b: 'b' }
o.merge!(o) { |key, value| "%#{ value }%" }

puts o.inspect
> { :a => "%a%", :b => "%b%" }
7
user1115652

次のようなRSpecでテストした後:

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

次のようにHash#map_valuesを実装できます。

class Hash
  def map_values
    Hash[map { |k, v| [k, yield(v)] }]
  end
end

関数は次のように使用できます。

{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
5
wedesoft