How to change Hash values?

134,885

Solution 1

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

or, if you'd prefer to do it non-destructively, and return a new hash instead of modifying my_hash:

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

This last version has the added benefit that you could transform the keys too.

Solution 2

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.

Solution 3

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

Like this:

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

Solution 4

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.

Solution 5

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!.

Share:
134,885
Admin
Author by

Admin

Updated on October 17, 2020

Comments

  • Admin
    Admin over 3 years

    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{ ... }
    
  • kch
    kch about 15 years
    that's good, but it should be noted that it only works for when j has a destructive method that acts unto itself. So it can't handle the general value.some_method case.
  • Chris Doggett
    Chris Doggett about 15 years
    Good point, and with his update, your second solution (non-destructive) is the best.
  • Marcus
    Marcus over 12 years
    The first suggestion is not appropriate; modifying an enumerable element while iterating leads to undefined behavior - this is valid in other languages also. Here's a reply from Matz (he replies to an example using an Array, but the answer is generic): j.mp/tPOh2U
  • Kelvin
    Kelvin over 12 years
    @Marcus I agree that in general such modification should be avoided. But in this case it's probably safe, because the modification doesn't change future iterations. Now if another key (that wasn't the one passed to the block) was assigned, then there would be trouble.
  • Kelvin
    Kelvin over 12 years
    @Adam @kch each_with_object is a more convenient version of inject when you don't want to end the block with the hash: my_hash.each_with_object({}) {|(k, v), h| h[k] = v.upcase }
  • rewritten
    rewritten about 10 years
    There is also a very neat syntax to convert a list of pairs into a hash, so you can write a_new_hash = Hash[ my_hash.map { |k, v| [k, v.upcase] } ]
  • roman-roman
    roman-roman almost 10 years
    with Ruby 2 you can write .to_h
  • Christian Di Lorenzo
    Christian Di Lorenzo almost 9 years
    I like that this method does not modify the original hash.
  • ioquatix
    ioquatix almost 8 years
    Is this efficient?
  • rj487
    rj487 almost 7 years
    great, it also comes with rails 4.2.1 apidock.com/rails/v4.2.1/Hash/transform_values
  • David Hempy
    David Hempy about 5 years
    See @potashin 's answer below for a better, modern solution.