2012-01-03 33 views
22

Tôi có một băm như:của Ruby dup/clone đệ quy

h = {'name' => 'sayuj', 
    'age' => 22, 
    'project' => {'project_name' => 'abc', 
        'duration' => 'prq'}} 

Tôi cần một dup của băm này, sự thay đổi không ảnh hưởng đến băm gốc.

Khi tôi cố gắng,

d = h.dup # or d = h.clone 
d['name'] = 'sayuj1' 
d['project']['duration'] = 'xyz' 

p d #=> {"name"=>"sayuj1", "project"=>{"duration"=>"xyz", "project_name"=>"abc"}, "age"=>22} 
p h #=> {"name"=>"sayuj", "project"=>{"duration"=>"xyz", "project_name"=>"abc"}, "age"=>22} 

Ở đây bạn sẽ nhìn thấy project['duration'] được thay đổi trong băm gốc vì project là một đối tượng băm.

Tôi muốn hàm băm là duped hoặc cloned đệ quy. Làm thế nào tôi có thể đạt được điều này?

Trả lời

39

Đây là cách bạn làm bản sao sâu trong Ruby

d = Marshal.load(Marshal.dump(h)) 
+2

Thao tác này tạo bản sao đầy đủ của tất cả các đối tượng được tham chiếu bởi 'h'. Điều này có thể là chính xác những gì là cần thiết bởi Sayuj cho chuỗi băm đơn giản. Với các đối tượng phức tạp hơn, điều này có thể không còn mong muốn nữa. Một khi có thể ghi đè phương thức 'Hash # dup' để đảo tất cả các hash trong' giá trị' đệ quy. Nhưng điều đó sẽ cần phải được mở rộng cho mọi loại đối tượng. –

+2

@HolgerChỉ cần: vâng, đó là lý do tại sao nó được gọi là "bản sao sâu" :-) –

+1

Tất nhiên. Tôi chỉ muốn đề cập rằng nó có thể làm nhiều hơn OP dự định (mặc dù nó có lẽ chỉ là tốt) :) Vì vậy, nó chỉ cho, tốt, tham khảo trong tương lai. –

1

Đây là một câu trả lời cho một câu hỏi hợp lý cũ, nhưng tôi đã xảy ra khi nó trong khi thực hiện một cái gì đó tương tự, nghĩ rằng tôi muốn kêu vang trong cho một phương pháp hiệu quả hơn .

Đối với đơn giản, hai mức băm sâu như trên, bạn cũng có thể làm một cái gì đó như thế này:

d = h.inject({}) {|copy, (key, value)| 
    copy[key] = value.dup rescue value; copy 
} 

Tôi chạy một thử nghiệm trên một băm băm với các yếu tố 4k, mỗi vài trăm byte, và nhanh hơn khoảng 50% so với Marshal.dump/load

Tất nhiên, nó không hoàn chỉnh, vì nó sẽ không hoạt động nếu bạn có giá trị băm, ví dụ, giá trị của trường 'project_name', nhưng với băm 2 mức đơn giản, nó hoạt động tốt/nhanh hơn.

2

Trong trường hợp Marchal#dump/load cặp đang không hoạt động, cho có một Hash 's phương pháp #deep_dup, vì vậy bạn có thể:

h = {'name' => 'sayuj', 
'age' => 22, 
'project' => {'project_name' => 'abc', 
       'duration' => 'prq'}} 

h1 = h.deep_dup 
+1

phương thức nên là h.deep_dup thay vì h.deep. dup – yopefonic

+0

Phương thức 'deep_dup' sẽ biến Lớp thành Lớp ẩn danh, không được khuyến nghị. –

+0

@TianChen ví dụ? –

0

Một lựa chọn khác là sử dụng gem full_dup (tiết lộ đầy đủ: Tôi là tác giả của viên đá quý đó) xử lý mảng, băm, cấu trúc và có thể mở rộng đến các lớp do người dùng định nghĩa.

Cách sử dụng:

require 'full_dup' 
# Other code omitted ... 
d = h.full_dup 

Cũng lưu ý full_dup để xử lý các mối quan hệ dữ liệu phức tạp bao gồm những người có vòng hoặc đệ quy.