You can create a new hash with the keys or values modified, indeed you can also add or delete keys, using inject (AKA, reduce). For example to produce a hash with stringified keys and upper case values:
fruit = { name: 'apple', color: 'green', shape: 'round' }
# => {:name=>"apple", :color=>"green", :shape=>"round"}
new_fruit = fruit.inject({}) { |memo, (k,v)| memo[k.to_s] = v.upcase; memo }
# => new_fruit is {"name"=>"APPLE", "color"=>"GREEN", "shape"=>"ROUND"}
Hash is an enumerable, in essence a collection of key/value pairs. Therefore is has methods such as each
, map
and inject
.
For every key/value pair in the hash the given block is evaluated, the value of memo on the first run is the seed value passed to inject
, in our case an empty hash, {}
. The value of memo
for subsequent evaluations is the returned value of the previous blocks evaluation, this is why we modify memo
by setting a key with a value and then return memo
at the end. The return value of the final blocks evaluation is the return value of inject
, in our case memo
.
To avoid the having to provide the final value, you could use each_with_object instead:
new_fruit = fruit.each_with_object({}) { |(k,v), memo| memo[k.to_s] = v.upcase }
Or even map:
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]
(See this answer for more details, including how to manipulate hashes in place.)