ハッシュのすべての値を変更して、値の前後に '%'を追加したいので、
{ :a=>'a' , :b=>'b' }
に変更する必要があります
{ :a=>'%a%' , :b=>'%b%' }
これを行う最良の方法は何ですか?
ハッシュのすべての値を変更して、値の前後に '%'を追加したいので、
{ :a=>'a' , :b=>'b' }
に変更する必要があります
{ :a=>'%a%' , :b=>'%b%' }
これを行う最良の方法は何ですか?
回答:
実際の文字列自体を適切に変更したい場合(おそらく、同じ文字列オブジェクトへの他の参照に影響を与える可能性があります):
# 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}%"] } ]
Hash.[]
配列ペアの配列を受け入れません。これには、偶数の直接引数が必要です(したがって、最初にスプラット)。
Hash#each
はキーと値の両方をブロックに渡します。この場合、私はキーを気にしなかったので、便利な名前を付けませんでした。変数名はアンダースコアで始まる場合があり、実際にはアンダースコアだけの場合もあります。これを行うことによるパフォーマンス上の利点はありません。これは、最初のブロック値では何もしないという、自己文書化された微妙なメモです。
my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
と、ブロックからハッシュを返す必要が
Ruby 2.1以降でできること
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
、sschmeck(stackoverflow.com/a/41508214/6451879)で指摘されているように、さらに使いやすくなっています。
Ruby 2.4でHash#transform_values!
使用できるメソッドが導入されました。
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
、レシーバーを変更しない(強打なしで)もあります。それ以外の場合は素晴らしい回答、ありがとう!
もう少し可読一map
その単一要素のハッシュの配列へとreduce
とそのmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(のmap
)ペアを作成し、次にを作成しますHash
。次に、reduce操作の各ステップで「メモ」を複製し、Hash
それに新しいキーと値のペアを追加します。少なくとも:merge!
in reduce
を使用してファイナルを適切に修正しHash
ます。そして最終的には、既存のオブジェクトの値を変更するのではなく、新しいオブジェクトを作成します。これは質問の答えではありません。
nil
場合に返されるthe_hash
このタスクには新しい「Railsウェイ」メソッドがあります:) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
。これは、今後の方法です。
オリジナルに副作用をもたらさない1つの方法:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
がハッシュをHash.map
返さない理由(結果として[key,value]
ペアの配列が新しいハッシュに変換される理由)を説明し、同じ一般的なパターンへの代替アプローチを提供するため、Hash#mapも興味深い読み物になる場合があります。
ハッピーコーディング。
[免責事項:Hash.map
Ruby 2.xでセマンティクスが変更されたかどうかはわかりません]
Hash.map
Ruby 2.xでセマンティクスが変更されたかどうかをマッツは知っていますか?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
Ruby 1.9(IIRC)には、名前に直接アクセスする必要がなく、機能するMap#merge
場合もあります。複雑な詳細がどのように異なるのかわかりません。
このように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%"}
ここでどのインプレースバリアントが最速か知りたければ、次のようになります。
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