如何在Ruby中优雅地重命名哈希中的所有键?

Cha*_*ory 91 ruby hash key

我有一个Ruby哈希:

ages = { "Bruce" => 32,
         "Clark" => 28
       }
Run Code Online (Sandbox Code Playgroud)

假设我有另一个替换名称的哈希,是否有一种优雅的方式来重命名所有键,以便我最终得到:

ages = { "Bruce Wayne" => 32,
         "Clark Kent" => 28
       }
Run Code Online (Sandbox Code Playgroud)

Jör*_*tag 182

ages = { "Bruce" => 32, "Clark" => 28 }
mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"}

ages.map {|k, v| [mappings[k], v] }.to_h
Run Code Online (Sandbox Code Playgroud)

  • 只需使用`mappings [k] || k`而不是上面的`mappings [k]`它将使键不在映射中. (28认同)
  • 虽然`to_h`仅适用于Ruby 2.0以上版本.在Ruby 1.9.3中,我通过将整个内容包装在`Hash [...]`中来实现 (2认同)

bar*_*olo 49

我喜欢JörgWMittag的答案,但它可以改进.

如果要重命名当前Hash的键,而不是使用重命名的键创建新的Hash,则以下代码段完全相同:

ages = { "Bruce" => 32, "Clark" => 28 }
mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"}

ages.keys.each { |k| ages[ mappings[k] ] = ages.delete(k) if mappings[k] }
ages
Run Code Online (Sandbox Code Playgroud)

还有一个优点是只重命名必要的密钥.

性能考虑:

根据Tin Man的回答,我的回答比JörgWMittag只用两把钥匙的Hash 快20%.对于具有许多键的Hashes,它可能会获得更高的性能,特别是如果只有几个键可以重命名.


ste*_*eel 10

each_with_objectRuby中也有未充分利用的方法:

ages = { "Bruce" => 32, "Clark" => 28 }
mappings = { "Bruce" => "Bruce Wayne", "Clark" => "Clark Kent" }

ages.each_with_object({}) { |(k, v), memo| memo[mappings[k]] = v }
Run Code Online (Sandbox Code Playgroud)


the*_*Man 8

只是为了看看速度更快:

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
Run Code Online (Sandbox Code Playgroud)

哪个输出:

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%
Run Code Online (Sandbox Code Playgroud)

注意: barbell的解决方案使用if mappings[k],如果mappings[k]结果为零值,将导致生成的哈希错误.


Gre*_*reg 5

我修补了类来处理嵌套的哈希和数组:

   #  Netsted Hash:
   # 
   #  str_hash = {
   #                "a"  => "a val", 
   #                "b"  => "b val",
   #                "c" => {
   #                          "c1" => "c1 val",
   #                          "c2" => "c2 val"
   #                        }, 
   #                "d"  => "d val",
   #           }
   #           
   # mappings = {
   #              "a" => "apple",
   #              "b" => "boss",
   #              "c" => "cat",
   #              "c1" => "cat 1"
   #           }
   # => {"apple"=>"a val", "boss"=>"b val", "cat"=>{"cat 1"=>"c1 val", "c2"=>"c2 val"}, "d"=>"d val"}
   #
   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
Run Code Online (Sandbox Code Playgroud)