[ruby] How to change Hash values?

I'd like to replace each value in a hash with value.some_method.

For example, for given a simple hash:

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

every value should be .upcased, so it looks like:

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

I tried #collect and #map but always just get arrays back. Is there an elegant way to do this?

UPDATE

Damn, I forgot: The hash is in an instance variable which should not be changed. I need a new hash with the changed values, but would prefer not to define that variable explicitly and then loop over the hash filling it. Something like:

new_hash = hash.magic{ ... }

This question is related to ruby syntax hash

The answer is


Ruby has the tap method (1.8.7, 1.9.3 and 2.1.0) that's very useful for stuff like this.

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"}

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"}

Rails-specific

In case someone only needs to call to_s method to each of the values and is not using Rails 4.2 ( which includes transform_values method link), you can do the following:

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"}

Note: The use of 'json' library is required.

Note 2: This will turn keys into strings as well


You may want to go a step further and do this on a nested hash. Certainly this happens a fair amount with Rails projects.

Here's some code to ensure a params hash is in 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  

There's a method for that in ActiveSupport v4.2.0. It's called transform_values and basically just executes a block for each key-value-pair.

Since they're doing it with a each I think there's no better way than to loop through.

hash = {sample: 'gach'}

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

Update:

Since Ruby 2.4.0 you can natively use #transform_values and #transform_values!.


If you know that the values are strings, you can call the replace method on them while in the cycle: this way you will change the value.

Altohugh this is limited to the case in which the values are strings and hence doesn't answer the question fully, I thought it can be useful to someone.


You can collect the values, and convert it from Array to Hash again.

Like this:

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

This will do it:

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

As opposed to inject the advantage is that you are in no need to return the hash again inside the block.


I do something like this:

new_hash = Hash[*original_hash.collect{|key,value| [key,value + 1]}.flatten]

This provides you with the facilities to transform the key or value via any expression also (and it's non-destructive, of course).


Try this function:

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

Since ruby 2.4.0 you can use native Hash#transform_values method:

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

There is also destructive Hash#transform_values! version.


Examples related to ruby

Uninitialized Constant MessagesController Embed ruby within URL : Middleman Blog Titlecase all entries into a form_for text field Ruby - ignore "exit" in code Empty brackets '[]' appearing when using .where find_spec_for_exe': can't find gem bundler (>= 0.a) (Gem::GemNotFoundException) How to update Ruby Version 2.0.0 to the latest version in Mac OSX Yosemite? How to fix "Your Ruby version is 2.3.0, but your Gemfile specified 2.2.5" while server starting Is the server running on host "localhost" (::1) and accepting TCP/IP connections on port 5432? How to update Ruby with Homebrew?

Examples related to syntax

What is the 'open' keyword in Swift? Check if returned value is not null and if so assign it, in one line, with one method call Inline for loop What does %>% function mean in R? R - " missing value where TRUE/FALSE needed " Printing variables in Python 3.4 How to replace multiple patterns at once with sed? What's the meaning of "=>" (an arrow formed from equals & greater than) in JavaScript? How can I fix MySQL error #1064? What do >> and << mean in Python?

Examples related to hash

php mysqli_connect: authentication method unknown to the client [caching_sha2_password] What is Hash and Range Primary Key? How to create a laravel hashed password Hashing a file in Python PHP salt and hash SHA256 for login password Append key/value pair to hash with << in Ruby Are there any SHA-256 javascript implementations that are generally considered trustworthy? How do I generate a SALT in Java for Salted-Hash? What does hash do in python? Hashing with SHA1 Algorithm in C#