Cách tìm khóa băm chứa giá trị khớp


198

Nếu tôi có hàm băm của khách hàng bên dưới , có cách nào nhanh chóng (không cần phải viết tập lệnh nhiều dòng) để lấy khóa được cung cấp mà tôi muốn khớp với client_id không? Ví dụ: Làm thế nào để lấy chìa khóa cho client_id == "2180"?

clients = {
  "yellow"=>{"client_id"=>"2178"}, 
  "orange"=>{"client_id"=>"2180"}, 
  "red"=>{"client_id"=>"2179"}, 
  "blue"=>{"client_id"=>"2181"}
}

Câu trả lời:


176

Bạn có thể sử dụng Số lượng # chọn :

clients.select{|key, hash| hash["client_id"] == "2180" }
#=> [["orange", {"client_id"=>"2180"}]]

Lưu ý rằng kết quả sẽ là một mảng của tất cả các giá trị khớp nhau, trong đó mỗi giá trị là một mảng của khóa và giá trị.


22
@Coderama Sự khác biệt giữa findselectfindtrả về kết quả khớp đầu tiên và select(được đặt bí danh là findAll) trả về tất cả các kết quả khớp.
Daniel Vandersluis

Tôi hiểu, vì vậy đây sẽ là tùy chọn an toàn hơn cho các trường hợp có nhiều hơn một trận đấu.
Coderama

1
Điều này tốt hơn là tạo ra một hàm băm hoàn toàn mới (bằng cách gọi invert) chỉ để tìm một mục.
Hejazi

3
Lưu ý rằng kể từ Ruby 1.9.3 , điều này sẽ trả về một hàm băm mới với các kết quả khớp. Nó sẽ không trả về một mảng, như đã từng sử dụng trong Ruby <= 1.8.7 . clients.select{|key, hash| hash["client_id"] == "2180" } # => {"orange"=>{"client_id"=>"2180"}}
AndrewS

Để lấy (các) khóa, chỉ cần đặtclients.select{|key, hash| hash["client_id"] == "2180" }.keys
Mirror318

407

Ruby 1.9 trở lên :

hash.key(value) => key

Ruby 1.8 :

Bạn đã có thể sử dụng hash.index

hsh.index(value) => key

Trả về khóa cho một giá trị nhất định. Nếu không tìm thấy, trả lại nil.

h = { "a" => 100, "b" => 200 }
h.index(200) #=> "b"
h.index(999) #=> nil

Vì vậy, để có được "orange", bạn chỉ có thể sử dụng:

clients.key({"client_id" => "2180"})

2
Điều này sẽ trở nên lộn xộn nếu băm có nhiều khóa, bởi vì bạn cần đưa toàn bộ hàm băm vào index.
Daniel Vandersluis

51
Hash#indexđược đổi tên thành Hash#keytrong Ruby 1.9
Vikrant Chaudhary

12
Lưu ý rằng điều này chỉ trả về kết quả khớp đầu tiên, nếu có nhiều cặp băm có cùng giá trị, nó sẽ trả về khóa đầu tiên có giá trị khớp.
Mike Campbell

47

Bạn có thể đảo ngược hàm băm. clients.invert["client_id"=>"2180"]trả lại"orange"


3
Đây cũng có vẻ là một cách thông minh (vì nó ngắn) để làm điều đó!
Coderama

đây là những gì tôi cần cho mảng mẫu (đối với các hộp được chọn) tạo ra hàm băm ngược
Joseph Le Brech

22

Bạn có thể sử dụng hashname.key (tên có giá trị)

Hoặc, một sự đảo ngược có thể theo thứ tự. new_hash = hashname.invertsẽ cung cấp cho bạn một new_hashcho phép bạn làm những việc truyền thống hơn.


3
Đây là cách thích hợp để làm điều đó trong các phiên bản gần đây (1.9+) của Ruby.
Lars Haugseth

#invertlà một ý tưởng thực sự tồi tệ trong trường hợp này, vì về cơ bản, bạn đang phân bổ bộ nhớ cho đối tượng băm vứt đi chỉ vì mục đích tìm khóa. Tùy thuộc vào kích thước băm, nó có tác động hiệu suất nghiêm trọng
Dr.Strangelove

15

thử cái này:

clients.find{|key,value| value["client_id"] == "2178"}.first

4
Điều này sẽ đưa ra một ngoại lệ nếu tìm thấy trả về con số không, bởi vì bạn không thể gọi .first on nil.
Schrockwell

1
Nếu sử dụng Rails, bạn có thể sử dụng .try(:first)thay vì .firstđể tránh các ngoại lệ (Nếu bạn đang mong đợi giá trị đó có thể bị thiếu).
mã hóa aaron

2
trong Ruby 2.3.0 +bạn có thể sử dụng bộ điều hướng an toàn &.first ở cuối khối để ngăn chặn ngoại lệ Nil
Gagan Gami


6

Từ các tài liệu:

  • (Đối tượng?) Phát hiện (ifnone = nil) {| obj | ...}
  • (Đối tượng?) Tìm (ifnone = nil) {| obj | ...}
  • (Đối tượng) phát hiện (ifnone = nil)
  • (Đối tượng) tìm (ifnone = nil)

Vượt qua từng mục trong enum để chặn. Trả về khối đầu tiên mà khối không sai. Nếu không có đối tượng khớp, gọi ifnone và trả về kết quả của nó khi được chỉ định hoặc trả về nil nếu không.

Nếu không có khối nào được đưa ra, thay vào đó, một điều tra viên được trả về.

(1..10).detect  {|i| i % 5 == 0 and i % 7 == 0 }   #=> nil
(1..100).detect {|i| i % 5 == 0 and i % 7 == 0 }   #=> 35

Điều này làm việc cho tôi:

clients.detect{|client| client.last['client_id'] == '2180' } #=> ["orange", {"client_id"=>"2180"}] 

clients.detect{|client| client.last['client_id'] == '999999' } #=> nil 

Xem: http://rubydoc.info/stdlib/core/1.9.2/Enumerable#find-instance_method


3

Cách tốt nhất để tìm khóa cho một giá trị cụ thể là sử dụng phương thức khóa có sẵn cho hàm băm ....

gender = {"MALE" => 1, "FEMALE" => 2}
gender.key(1) #=> MALE

Tôi hy vọng nó giải quyết vấn đề của bạn ...


2

Một cách tiếp cận khác tôi sẽ thử là sử dụng #map

clients.map{ |key, _| key if clients[key] == {"client_id"=>"2180"} }.compact 
#=> ["orange"]

Điều này sẽ trả về tất cả các lần xuất hiện của giá trị nhất định. Gạch dưới có nghĩa là chúng ta không cần mang theo giá trị của khóa để nó không được gán cho một biến. Mảng sẽ chứa nils nếu các giá trị không khớp - đó là lý do tại sao tôi đặt#compact ở cuối.


1

Đây là một cách dễ dàng để tìm các khóa của một giá trị nhất định:

    clients = {
      "yellow"=>{"client_id"=>"2178"}, 
      "orange"=>{"client_id"=>"2180"}, 
      "red"=>{"client_id"=>"2179"}, 
      "blue"=>{"client_id"=>"2181"}
    }

    p clients.rassoc("client_id"=>"2180")

... Và để tìm giá trị của một khóa đã cho:

    p clients.assoc("orange") 

nó sẽ cung cấp cho bạn cặp khóa-giá trị.

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.