Я хочу змінити кожне значення в хеші, щоб додати "%" до і після значення
{ :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 на місці - це
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
(for map
), а потім a Hash
. Тоді кожен крок операції зменшення дублюватиме «пам’ятку» Hash
та додавати до неї нову пару ключ-значення. Принаймні , використання :merge!
в reduce
модифікувати остаточне Hash
на місці. Зрештою, ви не змінюєте значення існуючого об'єкта, а створюєте новий об’єкт, що не є запитанням.
nil
якщо the_hash
порожній
Для цього завдання існує новий метод "Шлях рейлів" :) 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 # також може бути цікавим для прочитання, оскільки вона пояснює, чому Hash.map
не повертає хеш (саме тому результатний масив[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