How to change all the keys of a hash by a new set of given keys

后端 未结 7 1419
广开言路
广开言路 2020-12-04 19:04

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?

相关标签:
7条回答
  • 2020-12-04 19:39

    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"
    }
    
    0 讨论(0)
  • 2020-12-04 19:44

    Assuming you have a Hash which maps old keys to new keys, you could do something like

    hsh.transform_keys(&key_map.method(:[]))
    
    0 讨论(0)
  • 2020-12-04 19:55

    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}
    
    0 讨论(0)
  • 2020-12-04 19:57
    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} 
    
    0 讨论(0)
  • 2020-12-04 19:58

    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.

    0 讨论(0)
  • 2020-12-04 20:02

    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?"

    0 讨论(0)
提交回复
热议问题