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

198

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

clients = {
  "yellow"=>{"client_id"=>"2178"}, 
  "orange"=>{"client_id"=>"2180"}, 
  "red"=>{"client_id"=>"2179"}, 
  "blue"=>{"client_id"=>"2181"}
}
Coderama
источник

Ответы:

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"})
Aillyn
источник
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что позволяет вам делать вещи более традиционно.

boulder_ruby
источник
3
Это правильный способ сделать это в последних версиях (1.9+) Ruby.
Ларс Хаугсет
#invertЭто действительно плохая идея в этом случае, так как вы по сути выделяете память для одноразового хеш-объекта только ради поиска ключа. В зависимости от размера хэша это оказывает серьезное влияние на производительность
Dr.Strangelove
15

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

clients.find{|key,value| value["client_id"] == "2178"}.first
iNecas
источник
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

Rimian
источник
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") 

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

Салех Растани
источник