How to change all the keys of a hash by a new set of given keys
Solution 1
Ruby 2.5 has Hash#transform_keys! method. Example using a map of keys
h = {a: 1, b: 2, c: 3}
key_map = {a: 'A', b: 'B', c: 'C'}
h.transform_keys! {|k| key_map[k]}
# => {"A"=>1, "B"=>2, "C"=>3}
You can also use symbol#toproc shortcut with transform_keys Eg:
h.transform_keys! &:upcase
# => {"A"=>1, "B"=>2, "C"=>3}
Solution 2
Assuming you have a Hash
which maps old keys to new keys, you could do something like
hsh.transform_keys(&key_map.method(:[]))
Solution 3
i assume you want to change the hash keys
without changing the values:
hash = {
"nr"=>"123",
"name"=>"Herrmann Hofreiter",
"pferd"=>"010 000 777",
"land"=>"hight land"
}
header = ["aa", "bb", "cc", "dd"]
new_hash = header.zip(hash.values).to_h
Result:
{
"aa"=>"123",
"bb"=>"Herrmann Hofreiter",
"cc"=>"010 000 777",
"dd"=>"high land"
}
Solution 4
Another way to do it is:
hash = {
'foo' => 1,
'bar' => 2
}
new_keys = {
'foo' => 'foozle',
'bar' => 'barzle'
}
new_keys.values.zip(hash.values_at(*new_keys.keys)).to_h
# => {"foozle"=>1, "barzle"=>2}
Breaking it down:
new_keys
.values # => ["foozle", "barzle"]
.zip(
hash.values_at(*new_keys.keys) # => [1, 2]
) # => [["foozle", 1], ["barzle", 2]]
.to_h
# => {"foozle"=>1, "barzle"=>2}
It's benchmark time...
While I like the simplicity of Jörn's answer, I'm wasn't sure it was as fast as it should be, then I saw selvamani's comment:
require 'fruity'
HASH = {
'foo' => 1,
'bar' => 2
}
NEW_KEYS = {
'foo' => 'foozle',
'bar' => 'barzle'
}
compare do
mittag { HASH.dup.map {|k, v| [NEW_KEYS[k], v] }.to_h }
ttm { h = HASH.dup; NEW_KEYS.values.zip(h.values_at(*NEW_KEYS.keys)).to_h }
selvamani { h = HASH.dup; h.keys.each { |key| h[NEW_KEYS[key]] = h.delete(key)}; h }
end
# >> Running each test 2048 times. Test will take about 1 second.
# >> selvamani is faster than ttm by 39.99999999999999% ± 10.0%
# >> ttm is faster than mittag by 10.000000000000009% ± 10.0%
These are running very close together speed wise, so any will do, but 39% pays off over time so consider that. A couple answers were not included because there are potential flaws where they'd return bad results.
Solution 5
The exact solution would depend on the format that you have the new keys in (or if you can derive the new key from the old key.)
Assuming you have a hash h
whose keys you want to modify and a hash new_keys
that maps the current keys to the new keys you could do:
h.keys.each do |key|
h[new_keys[key]] = h[key] # add entry for new key
k.delete(key) # remove old key
end
Related videos on Youtube
JCLL
Updated on January 10, 2020Comments
-
JCLL over 4 years
How do I change all the keys of a hash by a new set of given keys?
Is there a way to do that elegantly?
-
CalebHC almost 13 yearsYou are the man! Your hash is totally awesome! Thanks again for this gem! :)
-
sawa over 11 yearsThis will remove a value for
key
whennew_keys
accidentally happens to returnkey
itself for somekey
. barbolos's answer to this question: stackoverflow.com/questions/4137824 overcomes this problem. -
Selvamani over 8 yearsinstead of this you can use
h.keys.each { |key| h[new_keys[key]] = h.delete(key)}
-
PJP about 8 yearsThis breaks if
key_map[k]
is nil. -
PJP about 8 yearsIt also assumes you have
key_map
defined as a hash of key/value pairs where the key is the old key and the value is the new key being swapped in. -
PJP about 8 years@Selvamani, see my answer, and create an answer from your comment.
-
Josh over 6 yearsI think calling merge for each key will be comparatively slow
-
Cary Swoveland over 6 years@Josh, you are correct. I re-ran @theTinMan's benchmark with my two methods added and obtained the following results: "selvamani is faster than ttm by 19.99% ± 1.0%; ttm is similar to caryewo (uses
each_with_object
); caryewo is similar to mittag; mittag is faster than caryred (usesreduce
) by 70.0% ± 10.0%". -
smileart over 6 yearsA moment of self promotion ☺️, but just to leave it here: if anything a bit more complex needed (like select particular keys at the same time or coerce values' types and so on), I put together a tiny lib: github.com/smileart/hash_remapper
-
JCLL about 6 yearsYou take advantage of the fact that order is preserved in hashes (since 2.0 I think) and by assumption (not given in my question) that new keys are also given in the same order as in the initial hash.
-
buncis about 3 yearsanyone could explain how the
&key_map.method(:[])
works?