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


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.