Как изменить значения хеширования?

Я хотел бы заменить каждый value на хэш с value.some_method.

Например, для заданного простого хэша:

{"a" => "b", "c" => "d"}` 

каждое значение должно быть .upcase d, поэтому оно выглядит так:

{"a" => "B", "c" => "D"}

Я пробовал #collect и #map, но всегда просто возвращаю массивы. Есть ли элегантный способ сделать это?

ОБНОВЛЕНИЕ

Черт, я забыл: хэш находится в переменной экземпляра, которая не должна быть изменена. Мне нужен новый хэш с измененными значениями, но я бы предпочел не указывать эту переменную явно, а затем перебирать хэш, заполняя ее. Что-то вроде:

new_hash = hash.magic{ ... }

Ответ 1

my_hash.each { |k, v| my_hash[k] = v.upcase } 

или, если вы предпочитаете делать это без разрушения и возвращать новый хэш вместо изменения my_hash:

a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h } 

Эта последняя версия имеет дополнительное преимущество, которое также можно было бы изменить.

Ответ 2

Вы можете собирать значения и преобразовывать их из Array в Hash снова.

Вот так:

config = Hash[ config.collect {|k,v| [k, v.upcase] } ]

Ответ 3

Это будет сделано:

my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }

В отличие от inject преимущество состоит в том, что вам не нужно возвращать хэш снова внутри блока.

Ответ 4

Так как ruby 2.4.0 вы можете использовать собственный метод Hash#transform_values:

hash = {"a" => "b", "c" => "d"}
new_hash = hash.transform_values(&:upcase)
# => {"a" => "B", "c" => "D"}

Существует также деструктивная Hash#transform_values! версия.

Ответ 5

Попробуйте эту функцию:

h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.

Ответ 6

Там есть метод для ActiveSupport v4.2.0. Он назывался transform_values и в основном просто выполняет блок для каждой пары ключ-значение.

Так как они делают это с each, я думаю, что нет лучшего способа, чем прокрутить.

hash = {sample: 'gach'}

result = {}
hash.each do |key, value|
  result[key] = do_stuff(value)
end

Ответ 7

Я делаю что-то вроде этого:

new_hash = Hash [* original_hash.collect {| ключ, значение | [ключ, значение + 1]}. flatten]

Это предоставляет вам возможности для преобразования ключа или значения с помощью любого выражения (и это, безразлично, конечно).

Ответ 8

Вы можете пойти еще дальше и сделать это на вложенном хэше. Разумеется, это справедливо для проектов Rails.

Здесь некоторый код для обеспечения хэша params находится в UTF-8:

  def convert_hash hash
    hash.inject({}) do |h,(k,v)|
      if v.kind_of? String
        h[k] = to_utf8(v) 
      else
        h[k] = convert_hash(v)
      end
      h
    end      
  end    

  # Iconv UTF-8 helper
  # Converts strings into valid UTF-8
  #
  # @param [String] untrusted_string the string to convert to UTF-8
  # @return [String] your string in UTF-8
  def to_utf8 untrusted_string=""
    ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
    ic.iconv(untrusted_string + ' ')[0..-2]
  end  

Ответ 9

Ruby имеет метод tap (1.8.7, 1.9.3 и 2.1.0), что очень полезно для таких вещей.

original_hash = { :a => 'a', :b => 'b' }
original_hash.clone.tap{ |h| h.each{ |k,v| h[k] = v.upcase } }
# => {:a=>"A", :b=>"B"}
original_hash # => {:a=>"a", :b=>"b"}

Ответ 10

new_hash = old_hash.merge(old_hash) do |_key, value, _value|
             value.upcase
           end

# old_hash = {"a" => "b", "c" => "d"}
# new_hash = {"a" => "B", "c" => "D"}

Ответ 11

Если вы знаете, что значения являются строками, вы можете вызвать метод replace на них во время цикла: таким образом вы будете измените значение.

Altohugh это ограничивается случаем, когда значения являются строками и, следовательно, не полностью отвечает на вопрос, я думал, что это может быть полезно кому-то.

Ответ 12

Rails конкретных

Если кому-то нужно только вызвать метод to_s для каждого из значений и не использует Rails 4.2 (который включает в себя метод transform_values ссылка), вы можете сделать следующее:

original_hash = { :a => 'a', :b => BigDecimal('23.4') }
#=> {:a=>"a", :b=>#<BigDecimal:5c03a00,'0.234E2',18(18)>}
JSON(original_hash.to_json)
#=> {"a"=>"a", "b"=>"23.4"}

Примечание. Необходимо использовать библиотеку "json".

Примечание 2: Это также превратит ключи в строки