2011-01-28 41 views
12

Làm cách nào tôi có thể hợp nhất hai băm không có khóa mới, có nghĩa là hợp nhất sẽ hợp nhất các khóa tồn tại trong cả hai băm?Làm cách nào để hợp nhất hai băm không có khóa mới

Ví dụ, tôi muốn những điều sau:

h = {:foo => "bar"} 
j = {:foo => "baz", :extra => "value"} 

puts h.merge(j) # {:foo => "baz"} 

Tôi đang tìm kiếm một cách thực sự trong sạch để làm điều này là thực hiện hiện tại của tôi là khá lộn xộn.

+0

Điều này giống như giao điểm băm. Bạn muốn điều gì xảy ra với cặp khóa/giá trị với các giá trị khác nhau? Ví dụ: h = {: foo => "value1"}; j = {: foo => "value2",: extra => "value"} –

+0

@Ron Gejman - Tôi chỉ muốn loại bỏ chúng. Nhưng bạn đã làm tôi ngạc nhiên. Có một số phương pháp băm/enumerable mà sẽ trả lại hai băm (một với các phím trùng lặp và một với các thức ăn thừa)? – elmt

+0

Không, nhưng thật dễ dàng để sử dụng một thứ gì đó dọc theo dòng câu trả lời của DigitalRoss. Chỉ cần lưu vào hai băm khác nhau — một cho các kết quả phù hợp và một cho các kết quả không phù hợp. –

Trả lời

14

Bạn có thể loại bỏ những khóa mà không được trong băm đầu tiên từ băm thứ hai, sau đó hợp nhất:

h.merge j.select { |k| h.keys.include? k } 

Không giống như thay thế sửa-out của tôi, đây là an toàn nếu bạn quyết định thay đổi nó vào một merge! hoặc update.

+0

(loại bỏ downvote của tôi, và thay đổi nó thành một upvote. Tôi đã nhầm lẫn về 'hợp nhất') –

+0

Điều này cho một cảnh báo trên máy của tôi—Hash.select mất trong một khối với hai đối số (| k, v |). –

+1

Ồ, rất tiếc, tôi viết Ruby 1.9. –

7

Câu trả lời của Yjerem hoạt động trong Ruby 1.9, nhưng không phải trong phiên bản 1.8.x. Trong 1.8.x, phương thức Hash#select trả về một mảng. Hash#reject trả về một băm.

h.reject { |k,v| !j.keys.include? k } 

Nếu bạn muốn giữ lại duy nhất cặp khóa-giá trị nào có giá trị giống nhau, bạn có thể làm điều này:

h.reject { |k,v| j[k] != h[k] } 

Trường hợp cạnh có nils. Nếu bạn đang lưu trữ nils trong Hash của bạn thì bạn phải làm điều này:

h.reject { |k,v| !j.has_key? k or j[k] != h[k] } 
+0

Cảm ơn bạn đã giải pháp 1.8 Ron Gejman. – elmt

+0

Đúng. Nó tương thích với 1.9. –

0
[h].inject({}) { |m,e| e.merge(j) { |k,o,n| m[k] = n }; m} 

hoặc

[{}].inject(h) { |m,e| m.merge(j) { |k,o,n| e[k] = n }; e} 

hoặc (có lẽ là tốt nhất, nhưng không phải là về mặt kỹ thuật một biểu hiện duy nhất) ...

t = {}; h.merge(j) { |k,o,n| t[k] = n }; t 
8

Nếu bạn đang sử dụng hành động ivesupport (một phần của đường ray), bạn có thể tận dụng 2 phương thức bổ sung vào Hash:

  • Hash#slice mất các phím mong muốn như các đối số riêng biệt (không phải là một mảng của các phím) và trả về một băm mới chỉ với các phím bạn hỏi cho.
  • Hash#except có các đối số giống như slice, nhưng trả về một băm mới bằng các khóa không có trong đối số.

tải đầu tiên activesupport:

require 'active_support/core_ext' 

Merge chỉ mục từ j có phím đã có trong h (ví dụ:sửa đổi, nhưng không thêm bất kỳ hoặc loại bỏ các mục trong h):

h.merge(j.slice(*h.keys)) 

Ví dụ:

ignore_new = ->(h, j) { h.merge(j.slice(* h.keys)) } 
ignore_new.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12}) 
# => {:a=>1, :b=>10, :c=>11} 

Lấy thức ăn thừa từ j mà không được ở h:

j.except(*h.keys) 

Phần thưởng:

Nếu bạn muốn ngã tư đúng, có nghĩa là bạn muốn có một kết quả mà chỉ có các phím được điểm chung giữa 2 băm, làm điều này:

h.merge(j).slice(* (h.keys & j.keys)) 

Ví dụ:

intersect = ->(h, j) { h.merge(j).slice(* (h.keys & j.keys)) } 
intersect.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12}) 
# => {:b=>10, :c=>11} 

và thức ăn thừa từ h rằng weren 't trong j:

h.except(*j.keys) 

bạn cũng có thể muốn sử dụng activesupport của HashWithIndifferentAccess nếu bạn muốn chuỗi & phím biểu tượng truy cập được coi là tương đương.

Lưu ý rằng không có ví dụ nào trên đây thay đổi băm ban đầu; băm mới được trả lại để thay thế.

-1

Cách tùy chỉnh nhiều hơn để làm điều này là:

h = {"foo"=> "bar"} 

j = {"foo" => "baz", "extra" => "value"} 


k = h.merge(j) 
result: {"foo"=>"baz", "extra"=>"value"} 

Dưới đây là chìa khóa "foo" trong băm thứ hai được trọng các "foo" trong hash.But đầu tiên nếu bạn muốn giữ lại tức là giá trị cũ thanh hoặc nếu bạn muốn giữ giá trị mới tức là "baz"? Bạn có thể làm điều gì đó như thế này:

k = h.merge(j){|key, old, new| old} 
result: {"foo"=>"bar", "extra"=>"value"} 


k = h.merge(j){|key, old, new| new} 

result: {"foo"=>"baz", "extra"=>"value"} 
+0

Câu hỏi yêu cầu một giao điểm của các phím. Kết quả phải là {: foo => "baz"}, không phải {"foo" => "baz", "extra" => "value"} – AndrewKS

Các vấn đề liên quan