Làm cách nào tôi có thể hợp nhất hai giá trị băm mà không ghi đè các khóa trùng lặp trong Ruby?


140

Có một cách dễ dàng hoặc thanh lịch để hợp nhất hai băm mà không ghi đè các khóa trùng lặp?

Đó là, nếu khóa có mặt trong hàm băm ban đầu, tôi không muốn thay đổi giá trị của nó.


Bạn có thực sự có nghĩa là mảng (ví dụ: ['a', 'b', 'c']) hoặc băm (ví dụ: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner

Xin lỗi, tôi đã nói về băm :)
Claudio Acciaresi

Câu trả lời:


232

Nếu bạn có hai giá trị băm optionsdefaults, và bạn muốn hợp nhất defaultsvào optionsmà không ghi đè các khóa hiện có, điều bạn thực sự muốn làm là ngược lại: hợp nhất optionsvào defaults:

options = defaults.merge(options)

Hoặc, nếu bạn đang sử dụng Rails, bạn có thể làm:

options.reverse_merge!(defaults)

Hoàn toàn đồng ý, cảm ơn rất nhiều vì Reverse_merge! phương pháp không biết điều đó :)
Claudio Acciaresi

Tại sao các dấu ngoặc đơn cần thiết ở đây? Bạn không thể chỉ làm các tùy chọn default.merge nó xuất hiện.
Donato

1
Họ đang phản đối reverse_merge!vì các vấn đề an ninh trong đường ray 5.1
Mirv - Matt

@ Mirv-Matt - Tôi không thấy thông báo khấu hao. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij

17

Có một cách trong thư viện Ruby tiêu chuẩn để hợp nhất Băm mà không ghi đè các giá trị hiện có hoặc gán lại hàm băm.

important_hash.merge!(defaults) { |key, important, default| important }

3

Nếu vấn đề của bạn là hàm băm ban đầu và hàm thứ hai đều có khóa trùng lặp và bạn không muốn ghi đè theo một trong hai hướng, bạn có thể phải thực hiện hợp nhất thủ công đơn giản với một số loại kiểm tra và xử lý va chạm:

hash2.each_key do |key|
  if ( hash1.has_key?(key) )
       hash1[ "hash2-originated-#{key}" ] = hash2[key]
  else
       hash1[key]=hash2[key]
  end
end

Rõ ràng, điều này rất thô sơ và giả sử rằng hash1 không có bất kỳ khóa nào được gọi là "hash2-originated-anything" - bạn có thể tốt hơn là chỉ cần thêm một số vào khóa để nó trở thành key1, key2 và cứ thế cho đến khi bạn nhấn vào một cái chưa có trong hash1. Ngoài ra, tôi đã không thực hiện bất kỳ viên ruby ​​nào trong vài tháng nên có lẽ điều đó không chính xác về mặt cú pháp, nhưng bạn sẽ có thể có được ý chính.

Hoặc xác định lại giá trị của khóa dưới dạng một mảng để hash1 [key] trả về giá trị ban đầu từ hash1 và giá trị từ hash2. Phụ thuộc vào những gì bạn muốn kết quả của bạn thực sự.


Nếu không giữ cả hai khóa, nhưng thêm các giá trị của cùng một khóa thì sao?
Tom KC Chiu

1
@ TomK.C.Chiu Điều đó phụ thuộc rất nhiều vào hoàn cảnh mà chúng ta không thể đánh giá được từ câu hỏi - điều gì xảy ra nếu các giá trị trong hash1 là chuỗi và hash2 là số nguyên? Đối với một số trường hợp có thể là một lựa chọn khả thi, nhưng thường thì nó sẽ gây ra sự cố - đề xuất sử dụng danh sách cho các giá trị hoạt động xung quanh điều này khá rõ ràng.
glenatron

0

Tại đây bạn có thể hợp nhất 2 hàm băm của mình bằng cách reverse_merge

order = {
 id: 33987,
 platform: 'web'
}

user = {
  name: 'Jhon Doe',
  email: 'jhon.doe@gmail.com' 
}
newHash = oder.reverse_merge!(user)
render json: { data: newHash, status: 200 }

0

Nếu bạn muốn hợp nhất hai băm optionsdefaultskhông ghi đè băm đích, bạn có thể kiểm tra xem selectkhóa đã có trong băm đích chưa. Đây là giải pháp Ruby thuần túy không có Rails:

options  = { "a" => 100, "b" => 200 }
defaults = { "b" => 254, "c" => 300 }
options.merge!(defaults.select{ |k,_| not options.has_key? k })

# output
# => {"a"=>100, "b"=>200, "c"=>300}

Hoặc nếu khóa có mặt, nhưng chứa nilvà bạn muốn ghi đè lên nó:

options.merge!(defaults.select{ |k,_| options[k].nil? })
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.