Как найти ключ хеша, содержащий соответствующее значение


198

Учитывая, что у меня есть хэш клиентов ниже , есть ли быстрый способ ruby ​​(без необходимости написания многострочного скрипта), чтобы получить ключ, если я хочу соответствовать client_id? Например, как получить ключ для client_id == "2180"?

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

Ответы:


177

Вы можете использовать Enumerable # select :

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

Обратите внимание, что результатом будет массив всех совпадающих значений, где каждое является массивом ключа и значения.


22
@Coderama Разница между findи в selectтом, что findвозвращает первое совпадение, и select(с псевдонимом findAll) возвращает все совпадения.
Даниэль Вандерслёйс

Понятно, так что это был бы более безопасный вариант для случаев, когда существует более одного совпадения.
Coderama

1
Это лучше, чем создавать новый хеш (вызывая invert), чтобы найти элемент.
Хиджази

3
Обратите внимание, что в Ruby 1.9.3 это вернет новый хеш с совпадениями. Он не будет возвращать массив, как это было в Ruby <= 1.8.7 . clients.select{|key, hash| hash["client_id"] == "2180" } # => {"orange"=>{"client_id"=>"2180"}}
AndrewS

Чтобы получить ключ (и), просто положитеclients.select{|key, hash| hash["client_id"] == "2180" }.keys
Mirror318

408

Ruby 1.9 и выше:

hash.key(value) => key

Ruby 1.8 :

Вы могли бы использовать hash.index

hsh.index(value) => key

Возвращает ключ для заданного значения. Если не найдено, возвращается nil.

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

Таким образом, чтобы получить "orange", вы можете просто использовать:

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

2
Это было бы немного грязно, если бы у хэшей было несколько ключей, потому что вам нужно было бы передать весь хеш index.
Даниэль Вандерслёйс

51
Hash#indexпереименован Hash#keyв Ruby 1.9
Викрант Чаудхари

12
Обратите внимание, что это возвращает только первое совпадение, если есть несколько хеш-пар с одинаковым значением, он вернет первый ключ с совпадающим значением.
Майк Кэмпбелл

47

Вы можете инвертировать хеш. clients.invert["client_id"=>"2180"]возвращается"orange"


3
Это также кажется умным способом (потому что он короткий) сделать это!
Кодерама

это то, что мне нужно для массивов форм (для блоков выбора), которые создают обратный хеш
Джозеф Ле Брех

22

Вы можете использовать hashname.key (valuename)

Или инверсия может быть в порядке. new_hash = hashname.invertдаст вам, new_hashчто позволяет вам делать вещи более традиционно.


3
Это правильный способ сделать это в последних версиях (1.9+) Ruby.
Ларс Хаугсет

#invertЭто действительно плохая идея в этом случае, так как вы по сути выделяете память для одноразового хеш-объекта только ради поиска ключа. В зависимости от размера хэша это оказывает серьезное влияние на производительность
Dr.Strangelove

15

попробуй это:

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

4
Это выдаст исключение, если find возвращает nil, потому что вы не можете вызвать .first для nil.
Шроквелл

1
Если вы используете Rails, вы можете использовать его .try(:first)вместо того, .firstчтобы избегать исключений (если вы ожидаете, что это возможно для пропущенного значения).
кодирование

2
в Ruby 2.3.0 +вы можете использовать безопасный навигатор &.first в конце блока , чтобы предотвратить от Nil Исключение
Gagan гами


6

Из документов:

  • (Объект?) Обнаружить (ifnone = nil) {| obj | ...}
  • (Объект?) Find (ifnone = nil) {| obj | ...}
  • (Объект) обнаружение (ifnone = ноль)
  • (Объект) найти (ifnone = ноль)

Пропускает каждую запись в enum для блокировки. Возвращает первое, для которого блок не является ложным. Если ни один объект не соответствует, вызывает ifnone и возвращает его результат, когда он указан, или возвращает nil в противном случае.

Если блок не задан, вместо него возвращается перечислитель.

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

Это сработало для меня:

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

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

Смотрите: http://rubydoc.info/stdlib/core/1.9.2/Enumerable#find-instance_method


3

Лучший способ найти ключ для определенного значения - использовать метод ключа, который доступен для хэша ....

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

Я надеюсь, что это решит вашу проблему ...


2

Другой подход, который я бы попробовал, заключается в использовании #map

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

Это вернет все вхождения заданного значения. Подчеркивание означает, что нам не нужно переносить значение ключа, чтобы его нельзя было присвоить переменной. Массив будет содержать нули, если значения не совпадают - поэтому я поставил #compactв конце.


1

Вот простой способ найти ключи заданного значения:

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

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

... и найти значение данного ключа:

    p clients.assoc("orange") 

это даст вам пару ключ-значение.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.