如何优雅地重命名ruby中的所有键?
我有一个Ruby哈希:
ages = { "Bruce" => 32, "Clark" => 28 }
假设我有另一个散列的replace名称,是否有一个优雅的方式来重命名所有的键,以便我最终:
ages = { "Bruce Wayne" => 32, "Clark Kent" => 28 }
ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.map {|k, v| [mappings[k], v] }.to_h
我喜欢JörgW Mittag的回答,但是可以改进。
如果你想重命名当前哈希键,而不是用重命名键创build一个新的哈希,下面的代码片段完全是这样的:
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
只有重命名必要的键也有好处。
性能考虑:
根据锡文的回答,我的答案比JörgW Mittag的答案快了20% ,只有两个键。 对于具有多个键的哈希,性能可能会更高,特别是如果只有几个键被重命名。
只是为了看看什么更快:
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
哪些产出:
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%
警告: if mappings[k]
导致杠铃的解决scheme,那么如果mappings[k]
导致零值,将导致产生的散列错误。
Ruby中还有利用不足的each_with_object
方法:
ages = { "Bruce" => 32, "Clark" => 28 } mappings = { "Bruce" => "Bruce Wayne", "Clark" => "Clark Kent" } ages.each_with_object({}) { |(k, v), memo| memo[mappings[k]] = v }
我猴子修补类处理嵌套哈希和数组:
# 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
如果映射哈希小于数据哈希值,则在映射上迭代。 这对重命名大哈希中的几个字段很有用:
class Hash def rekey(h) dup.rekey! h end def rekey!(h) h.each { |k, newk| store(newk, delete(k)) if has_key? k } self end end ages = { "Bruce" => 32, "Clark" => 28, "John" => 36 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} p ages.rekey! mappings
您可能希望使用Object#轻击以避免在键被修改后需要返回ages
:
ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.tap {|h| h.keys.each {|k| (h[mappings[k]] = h.delete(k)) if mappings.key?(k)}} #=> {"Bruce Wayne"=>32, "Clark Kent"=>28}
Facets gem提供了一个完全符合你想要的方法。
只要你可以依赖Facets gem,就可以传递一个hash映射到rekey
,它将返回一个新的hash值和新的键值:
require 'facets/hash/rekey' ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.rekey(mappings) => {"Bruce Wayne"=>32, "Clark Kent"=>28}
如果你想修改年龄哈希值,你可以使用rekey!
版:
ages.rekey!(mappings) ages => {"Bruce Wayne"=>32, "Clark Kent"=>28}
ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages = mappings.inject({}) {|memo, mapping| memo[mapping[1]] = ages[mapping[0]]; memo} puts ages.inspect
>> x={ :a => 'qwe', :b => 'asd'} => {:a=>"qwe", :b=>"asd"} >> rename={:a=>:qwe} => {:a=>:qwe} >> rename.each{|old,new| x[new] = x.delete old} => {:a=>:qwe} >> x => {:b=>"asd", :qwe=>"qwe"}
这将通过重命名哈希循环。
我使用它来允许Cucumber表中的“友好”名称被parsing为类属性,Factory Girl可以创build一个实例:
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
为什么它的价值,黄瓜表看起来像这样:
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 |
而map_to_keys
看起来像这样:
{ "Name" => "name", "Subdomain" => "subdomain", "Phone Number" => "phone_number", "Address" => "address", "Billing Contact" => "billing_contact", "Billing Address" => "billing_address" }