Пожалуйста, уточните, если вы хотите изменить исходные строковые объекты, просто изменить исходные объекты или ничего не изменять.
Phrogz
1
Тогда вы приняли неправильный ответ. (Не обижайтесь на @pst, поскольку я лично также выступаю за программирование в функциональном стиле вместо мутирования объектов.)
Phrogz
Но все же подход был очень приятным
theReverseFlick
Ответы:
178
Если вы хотите, чтобы сами фактические строки мутировали на месте (возможно и желательно влияя на другие ссылки на те же строковые объекты):
# 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 Hash.[]не принимает массив пар массивов, ему требуется четное количество прямых аргументов (отсюда и сплат).
Phrogz
2
На самом деле, Hash. [Key_value_pairs] был введен в 1.8.7, поэтому только Ruby 1.8.6 не нуждается в сплате и выравнивании.
Марк-Андре Лафортун
2
@Aupajo возвращает Hash#eachключ и значение для блока. В данном случае меня не заботил ключ, и я не назвал его полезным. Имена переменных могут начинаться с подчеркивания, а на самом деле могут быть только подчеркиванием. В этом нет никакого выигрыша в производительности, это всего лишь тонкое самодокументированное примечание, что я ничего не делаю с этим первым значением блока.
Phrogz
1
Я думаю, что вы имеете в виду my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, должны вернуть хэш из блока
aceofspades
1
В качестве альтернативы вы можете использовать метод each_value, который немного проще для понимания, чем использование подчеркивания для значения неиспользованного ключа.
Спасибо, на самом деле было то, что я искал. Не знаю, почему за тебя так мало проголосовали.
simperreault
7
Это, однако, очень медленно и очень требовательно к оперативной памяти. Входной Hash итерируется для получения промежуточного набора вложенных массивов, которые затем преобразуются в новый Hash. Игнорируя пиковое использование ОЗУ, время выполнения намного хуже - сравнение этого с решениями для модификации на месте в другом ответе показывает 2,5 с 1,5 с за то же число итераций. Поскольку Ruby является сравнительно медленным языком, избегать медленных кусочков медленного языка имеет большой смысл :-)
Эндрю Ходжкинсон
2
@AndrewHodgkinson, хотя в целом я согласен и не выступаю за то, чтобы не обращать внимания на производительность во время выполнения, разве отслеживание всех этих ловушек производительности не становится болью и не идет вразрез с философией ruby о «производительности сначала разработчиков»? Я думаю, что это не комментарий для вас, а скорее общий комментарий к возможному парадоксу, к которому мы приводим использование рубина.
elsurudo
4
Проблема в том, что мы уже отказались от производительности, приняв решение даже использовать ruby, так что же изменит этот «маленький кусочек»? Это скользкий склон, не так ли? Для протокола, я предпочитаю это решение принятому ответу с точки зрения читабельности.
Не совсем верно: новые строки выделены. Тем не менее, интересное решение, которое эффективно. +1
Phrogz
@Phrogz хорошая точка зрения; Я обновил ответ. В общем случае нельзя избежать выделения значений, поскольку не все преобразования значений могут быть выражены в виде мутаторов, таких как gsub!.
Сим
3
Так же, как мой ответ, но с другим синонимом, я согласен, что updateпередает намерение лучше, чем merge!. Я думаю, что это лучший ответ.
1
Если вы не используете k, используйте _вместо этого.
sekrett
28
Чуть более читаемый, mapэто массив одноэлементных хешей и reduceчто сmerge
Это крайне неэффективный способ обновления значений. Для каждой пары значений сначала создается пара Array(для map), а затем a Hash. Затем каждый шаг операции уменьшения будет дублировать «памятку» Hashи добавлять новую пару ключ-значение. По крайней мере , использование :merge!в reduceмодифицировать окончательное Hashна месте. И, в конце концов, вы не изменяете значения существующего объекта, а создаете новый объект, что не является вопросом вопроса.
Один метод, который не вводит побочные эффекты к оригиналу:
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]
Я не люблю побочные эффекты, но +1 за подход :) Есть each_with_objectв Ruby 1.9 (IIRC), который избегает необходимости прямого доступа к имени и Map#mergeтакже может работать. Не уверен, как отличаются сложные детали.
1
Первоначальный хэш изменен - это нормально, если поведение ожидается, но может вызвать тонкие проблемы, если «забыто». Я предпочитаю уменьшить изменчивость объекта, но это не всегда может быть практичным. (Ruby вряд ли является языком без побочных эффектов ;-)
8
Hash.merge! самое чистое решение
o ={ a:'a', b:'b'}
o.merge!(o){|key, value|"%#{ value }%"}
puts o.inspect
>{:a =>"%a%",:b =>"%b%"}
Ответы:
Если вы хотите, чтобы сами фактические строки мутировали на месте (возможно и желательно влияя на другие ссылки на те же строковые объекты):
Если вы хотите, чтобы хеш изменился на месте, но не хотите влиять на строки (вы хотите, чтобы он получал новые строки):
Если вы хотите новый хеш:
источник
Hash.[]
не принимает массив пар массивов, ему требуется четное количество прямых аргументов (отсюда и сплат).Hash#each
ключ и значение для блока. В данном случае меня не заботил ключ, и я не назвал его полезным. Имена переменных могут начинаться с подчеркивания, а на самом деле могут быть только подчеркиванием. В этом нет никакого выигрыша в производительности, это всего лишь тонкое самодокументированное примечание, что я ничего не делаю с этим первым значением блока.my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, должны вернуть хэш из блокаВ Ruby 2.1 и выше вы можете сделать
источник
#transform_values!
как указано в sschmeck ( stackoverflow.com/a/41508214/6451879 ).Ruby 2.4 представил метод
Hash#transform_values!
, который вы можете использовать.источник
Hash#transform_values
(без взрыва), который не изменяет приемник. В противном случае отличный ответ, спасибо!reduce
:-pЛучший способ изменить значения хэша на месте
Меньше кода и четких намерений. Также быстрее, потому что никакие новые объекты не выделяются за пределы значений, которые должны быть изменены.
источник
gsub!
.update
передает намерение лучше, чемmerge!
. Я думаю, что это лучший ответ.k
, используйте_
вместо этого.Чуть более читаемый,
map
это массив одноэлементных хешей иreduce
что сmerge
источник
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(дляmap
), а затем aHash
. Затем каждый шаг операции уменьшения будет дублировать «памятку»Hash
и добавлять новую пару ключ-значение. По крайней мере , использование:merge!
вreduce
модифицировать окончательноеHash
на месте. И, в конце концов, вы не изменяете значения существующего объекта, а создаете новый объект, что не является вопросом вопроса.nil
еслиthe_hash
пустДля этой задачи существует новый метод «Rails way» :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
источник
Hash#transform_values
. Это должно быть способом идти с этого момента.Один метод, который не вводит побочные эффекты к оригиналу:
Hash # map также может быть интересным, поскольку объясняет, почему
Hash.map
не возвращает Hash (именно поэтому результирующий массив[key,value]
пар преобразуется в новый Hash) и предоставляет альтернативные подходы к тому же общему шаблону.Удачного кодирования.
[Отказ от ответственности: я не уверен, если
Hash.map
семантика изменяется в Ruby 2.x]источник
Hash.map
семантика меняется в Ruby 2.x?источник
each_with_object
в Ruby 1.9 (IIRC), который избегает необходимости прямого доступа к имени иMap#merge
также может работать. Не уверен, как отличаются сложные детали.Hash.merge! самое чистое решение
источник
После тестирования с RSpec вот так:
Вы можете реализовать Hash # map_values следующим образом:
Затем функцию можно использовать так:
источник
Если вам интересно, какой вариант на месте является самым быстрым, то здесь:
источник