How to change all the keys of a hash by a new set of given keys
How do I change all the keys of a hash by a new set o开发者_开发技巧f given keys?
Is there a way to do that elegantly?
Assuming you have a Hash
which maps old keys to new keys, you could do something like
hsh.transform_keys(&key_map.method(:[]))
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}
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"
}
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.
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
If you also worry about performance, this is faster:
hsh.keys.each { |k| hsh[ key_map[k] ] = hsh.delete(k) if key_map[k] }
You don't create a new Hash and you rename only the necessary keys. That gives you better performance.
You can find more details in "How to elegantly rename all keys in a hash in Ruby?"
h = { 'foo'=>1, 'bar'=>2 }
key_map = { 'foo'=>'foozle', 'bar'=>'barzle' }
h.each_with_object({}) { |(k,v),g| g[key_map[k]]=v }
#=> {"foozle"=>1, "barzle"=>2}
or
h.reduce({}) { |g,(k,v)| g.merge(key_map[k]=>v) }
#=> {"foozle"=>1, "barzle"=>2}
精彩评论