I liked Jörg W Mittag's answer, but if you want to rename the keys of your current Hash and not to create a new Hash with the renamed keys, the following snippet does exactly that:
There's also the advantage of only renaming the necessary keys.
Performance considerations:
Based on the Tin Man's answer, my answer is about 20% faster than Jörg W Mittag's answer for a Hash with only two keys. It may get even higher performance for Hashes with many keys, specially if there are just a few keys to be renamed.
require 'fruity'
AGES = { "Bruce" => 32, "Clark" => 28 }
MAPPINGS = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"}
def jörg_w_mittag_test(ages, mappings)
Hash[ages.map {|k, v| [mappings[k], v] }]
end
require 'facets/hash/rekey'
def tyler_rick_test(ages, mappings)
ages.rekey(mappings)
end
def barbolo_test(ages, mappings)
ages.keys.each { |k| ages[ mappings[k] ] = ages.delete(k) if mappings[k] }
ages
end
class Hash
def tfr_rekey(h)
dup.tfr_rekey! h
end
def tfr_rekey!(h)
h.each { |k, newk| store(newk, delete(k)) if has_key? k }
self
end
end
def tfr_test(ages, mappings)
ages.tfr_rekey mappings
end
class Hash
def rename_keys(mapping)
result = {}
self.map do |k,v|
mapped_key = mapping[k] ? mapping[k] : k
result[mapped_key] = v.kind_of?(Hash) ? v.rename_keys(mapping) : v
result[mapped_key] = v.collect{ |obj| obj.rename_keys(mapping) if obj.kind_of?(Hash)} if v.kind_of?(Array)
end
result
end
end
def greg_test(ages, mappings)
ages.rename_keys(mappings)
end
compare do
jörg_w_mittag { jörg_w_mittag_test(AGES.dup, MAPPINGS.dup) }
tyler_rick { tyler_rick_test(AGES.dup, MAPPINGS.dup) }
barbolo { barbolo_test(AGES.dup, MAPPINGS.dup) }
greg { greg_test(AGES.dup, MAPPINGS.dup) }
end
Which outputs:
Running each test 1024 times. Test will take about 1 second.
barbolo is faster than jörg_w_mittag by 19.999999999999996% ± 10.0%
jörg_w_mittag is faster than greg by 10.000000000000009% ± 10.0%
greg is faster than tyler_rick by 30.000000000000004% ± 10.0%
Caution: barbell's solution uses if mappings[k], which will cause the resulting hash to be wrong if mappings[k] results in a nil value.
I used this to allow "friendly" names in a Cucumber table to be parsed into class attributes such that Factory Girl could create an instance:
Given(/^an organization exists with the following attributes:$/) do |table|
# Build a mapping from the "friendly" text in the test to the lower_case actual name in the class
map_to_keys = Hash.new
table.transpose.hashes.first.keys.each { |x| map_to_keys[x] = x.downcase.gsub(' ', '_') }
table.transpose.hashes.each do |obj|
obj.keys.each { |k| obj[map_to_keys[k]] = obj.delete(k) if map_to_keys[k] }
create(:organization, Rack::Utils.parse_nested_query(obj.to_query))
end
end
For what it's worth, the Cucumber table looks like this:
Background:
And an organization exists with the following attributes:
| Name | Example Org |
| Subdomain | xfdc |
| Phone Number | 123-123-1234 |
| Address | 123 E Walnut St, Anytown, PA 18999 |
| Billing Contact | Alexander Hamilton |
| Billing Address | 123 E Walnut St, Anytown, PA 18999 |