Я хочу изменить каждое значение в хэше, чтобы добавить «%» до и после значения так
{ :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
(без взрыва), который не изменяет приемник. В противном случае отличный ответ, спасибо!
Лучший способ изменить значения хэша на месте
hash.update(hash){ |_,v| "%#{v}%" }
Меньше кода и четких намерений. Также быстрее, потому что никакие новые объекты не выделяются за пределы значений, которые должны быть изменены.
gsub!
.
update
передает намерение лучше, чем merge!
. Я думаю, что это лучший ответ.
k
, используйте _
вместо этого.
Чуть более читаемый, map
это массив одноэлементных хешей и reduce
что сmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(для map
), а затем a Hash
. Затем каждый шаг операции уменьшения будет дублировать «памятку» Hash
и добавлять новую пару ключ-значение. По крайней мере , использование :merge!
в reduce
модифицировать окончательное Hash
на месте. И, в конце концов, вы не изменяете значения существующего объекта, а создаете новый объект, что не является вопросом вопроса.
nil
если the_hash
пуст
Для этой задачи существует новый метод «Rails way» :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
. Это должно быть способом идти с этого момента.
Один метод, который не вводит побочные эффекты к оригиналу:
h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]
Hash # map также может быть интересным, поскольку объясняет, почему Hash.map
не возвращает Hash (именно поэтому результирующий массив [key,value]
пар преобразуется в новый Hash) и предоставляет альтернативные подходы к тому же общему шаблону.
Удачного кодирования.
[Отказ от ответственности: я не уверен, если 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