Reputation:
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 .upcase
d, 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{ ... }
Upvotes: 148
Views: 156257
Reputation: 44601
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.
Upvotes: 137
Reputation: 8755
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!
.
Upvotes: 12
Reputation: 1654
You can collect the values, and convert it from Array to Hash again.
Like this:
config = Hash[ config.collect {|k,v| [k, v.upcase] } ]
Upvotes: 37
Reputation: 11
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"}
Upvotes: 1
Reputation: 1301
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.
Upvotes: 1
Reputation: 4855
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
Upvotes: 1
Reputation: 13262
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"}
Upvotes: 1
Reputation: 29553
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.
Upvotes: 25
Reputation: 20777
Try this function:
h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.
Upvotes: 10
Reputation: 51
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
Upvotes: 4
Reputation:
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).
Upvotes: 1
Reputation: 79650
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.
Upvotes: 236