2010-11-09 41 views

Trả lời

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

ages.map {|k, v| [mappings[k], v] }.to_h 
+0

Cảm ơn, điều này thật tuyệt! Bây giờ, nếu tôi chỉ muốn thay đổi một số tên khóa, có cách nào để kiểm tra nếu ánh xạ tồn tại cho khóa không? – Chanpory

+28

Chỉ cần sử dụng 'ánh xạ [k] || k' thay vì 'ánh xạ [k]' ở trên và nó sẽ để lại các phím không có trong ánh xạ. –

+0

Rực rỡ! THanks rất nhiều :-) – Chanpory

1
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 
+0

lứa tuổi = ánh xạ. inject ({}) {| memo, (old_key, new_key) | ghi nhớ [new_key] = độ tuổi [old-key]; memo} – frogstarr78

5

Tôi đã vá lớp học để xử lý các mâu thuẫn và mảng lồng nhau:

# 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 
+0

Rất hữu ích. Thích nghi nó với nhu cầu của tôi để làm cho các trường hợp lạc đà phím phong cách nhấn mạnh. – idStar

+0

đẹp! nó có thể linh hoạt hơn để kiểm tra '.responds_to? (: rename_keys)' thay vì '.kind_of? (Hash)', và tương đương với 'Array', bạn nghĩ sao? – caesarsol

48

Tôi thích câu trả lời của Jörg W Mittag, nhưng nó có thể được cải thiện.

Nếu bạn muốn đổi tên các phím của Hash hiện tại của bạn và không để tạo ra một Hash mới với các phím đổi tên, đoạn sau thực hiện chính xác rằng:

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 

Ngoài ra còn có lợi thế là chỉ đổi tên cần thiết phím.

cân nhắc Hiệu suất:

Dựa trên câu trả lời the Tin Man 's, câu trả lời của tôi là nhanh hơn khoảng 20% ​​ hơn câu trả lời Jörg W Mittag cho một Hash với chỉ có hai phím. Nó có thể nhận được hiệu suất cao hơn cho Hashes với nhiều khóa, đặc biệt nếu chỉ có một vài phím để được đổi tên.

+0

Tôi thích điều này. Một trong những gotcha mà đánh tôi đã được tôi sử dụng trong as_json() gọi, và mặc dù các phím thuộc tính chính đã được chuyển đổi thành chuỗi, các options.merge (: phương pháp => [: blah]) thì đó là một chìa khóa trong bản đồ không phải là một chuỗi. – peterept

+1

@peterept bạn có thể thử tùy chọn.with_indifferent_access.merge (: phương thức => [: blah]). Điều này sẽ làm cho các tùy chọn truy cập chuỗi hoặc biểu tượng như các phím. – barbolo

+0

Yêu câu trả lời ... nhưng tôi nhầm lẫn về cách thức này thực sự hoạt động. Giá trị được đặt trên mỗi bộ như thế nào? –

3

Nếu ánh xạ Hash sẽ nhỏ hơn dữ liệu Hash sau đó lặp lại trên ánh xạ thay thế. Đây là hữu ích cho đổi tên một vài lĩnh vực trong một Hash lớn:

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 
2

Các Facets đá quý cung cấp một phương pháp rekey thực hiện chính xác những gì bạn đang mong muốn.

Chừng nào bạn đang okay với một sự phụ thuộc vào các Facets đá quý, bạn có thể vượt qua một hash của ánh xạ để rekey và nó sẽ trả về một băm mới với các phím mới:

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} 

Nếu bạn muốn để sửa đổi lứa tuổi băm tại chỗ, bạn có thể sử dụng phiên bản rekey!:

ages.rekey!(mappings) 
ages 
=> {"Bruce Wayne"=>32, "Clark Kent"=>28} 
8

Chỉ cần để xem những gì là nhanh hơn:

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 

Những kết quả đầu ra:

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% 

Chú ý: giải pháp barbell của sử dụng if mappings[k], mà sẽ gây ra băm kết quả là sai nếu mappings[k] kết quả trong một giá trị nil.

0
>> 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"} 

Điều này sẽ chỉ thông qua băm tên.

0

tôi sử dụng này cho phép "thân thiện" tên trong một bảng Dưa chuột được phân tách thành lớp thuộc tính như vậy mà Factory Girl có thể tạo ra một thể hiện:

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 

Đối với những gì nó có giá trị, bảng Dưa chuột trông như thế này :

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 trông như thế này:

{ 
       "Name" => "name", 
      "Subdomain" => "subdomain", 
     "Phone Number" => "phone_number", 
      "Address" => "address", 
    "Billing Contact" => "billing_contact", 
    "Billing Address" => "billing_address" 
} 
+0

Cảm ơn bạn đã chỉnh sửa, @the Tin Man –

8

Có là được khai thác each_with_object phương pháp trong Ruby cũng như:

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

ages.each_with_object({}) { |(k, v), memo| memo[mappings[k]] = v } 
+0

'each_with_object' chắc chắn là không được sử dụng, và rõ ràng hơn và dễ nhớ hơn so với' inject'. Đó là một bổ sung chào mừng khi nó được giới thiệu. –

+0

Tôi nghĩ đây là câu trả lời hay nhất. Bạn cũng có thể sử dụng '|| k' để xử lý trường hợp ánh xạ không có khóa tương ứng: '' 'ages.each_with_object ({}) {| (k, v), ghi nhớ | memo [ánh xạ [k] || k] = v} '' ' – coisnepe

2

Bạn có thể muốn sử dụng Object#tap để tránh sự cần thiết phải trở ages sau khi các phím đã được sửa đổi:

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} 
Các vấn đề liên quan