Como encontrar uma chave de hash contendo um valor correspondente

198

Dado que tenho o hash de clientes abaixo , existe uma maneira rápida de ruby ​​(sem a necessidade de escrever um script de várias linhas) para obter a chave, pois quero corresponder ao client_id? Por exemplo, como obter a chave client_id == "2180"?

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

Respostas:

176

Você pode usar Enumerable # select :

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

Observe que o resultado será uma matriz de todos os valores correspondentes, onde cada um é uma matriz da chave e do valor.

Daniel Vandersluis
fonte
22
@Coderama A diferença entre finde selecté que findretorna a primeira correspondência e select(com o apelido de findAll) retorna todas as correspondências.
Daniel Vandersluis
Entendo, essa seria a opção mais segura para casos em que há mais de uma correspondência.
Coderama
1
Isso é melhor do que criar um novo hash (chamando invert) apenas para encontrar um item.
Hejazi 25/01
3
Observe que a partir do Ruby 1.9.3 , isso retornará um novo hash com as correspondências. Ele não retornará uma matriz, como costumava no Ruby <= 1.8.7 . clients.select{|key, hash| hash["client_id"] == "2180" } # => {"orange"=>{"client_id"=>"2180"}}
Andrews
Para obter a chave (s), basta colocarclients.select{|key, hash| hash["client_id"] == "2180" }.keys
Mirror318
407

Ruby 1.9 e superior:

hash.key(value) => key

Ruby 1.8 :

Você poderia usar hash.index

hsh.index(value) => key

Retorna a chave para um determinado valor. Se não encontrado, retorna nil.

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

Então, para obter "orange", você pode simplesmente usar:

clients.key({"client_id" => "2180"})
Aillyn
fonte
2
Isso ficaria meio confuso se os hashes tivessem várias chaves, porque você precisaria fornecer todo o hash index.
Daniel Vandersluis
51
Hash#indexé renomeado para Hash#keyRuby 1.9
Vikrant Chaudhary 12/10
12
Observe que isso retorna apenas a primeira correspondência; se houver vários pares de hash com o mesmo valor, retornará a primeira chave com um valor correspondente.
Mike Campbell
47

Você pode inverter o hash. clients.invert["client_id"=>"2180"]retorna"orange"

Peter DeWeese
fonte
3
Isso também parece uma maneira inteligente (porque é curta) de fazê-lo!
Coderama 25/09/10
isto é o que eu preciso para matrizes de formulário (para caixas de seleção) que criam um hash para trás
Joseph Le Brech
22

Você pode usar hashname.key (nome do valor)

Ou, uma inversão pode estar em ordem. new_hash = hashname.invertlhe dará um new_hashque permite fazer as coisas de maneira mais tradicional.

boulder_ruby
fonte
3
Essa é a maneira correta de fazer isso nas versões recentes (1.9+) do Ruby.
Lars Haugseth
#inverté uma péssima idéia nesse caso, pois você está essencialmente alocando memória para o objeto hash descartável apenas para encontrar uma chave. Dependendo do tamanho do hash ter graves impacto no desempenho
Dr.Strangelove
15

tente isto:

clients.find{|key,value| value["client_id"] == "2178"}.first
iNecas
fonte
4
Isso lançará uma exceção se a descoberta retornar nulo, porque você não pode chamar .primeiro nulo.
Schrockwell
1
Se você estiver usando o Rails, poderá usar em .try(:first)vez de .firstevitar exceções (se você espera que o valor esteja ausente).
aaron-coding
2
em Ruby 2.3.0 +que você pode usar navegador seguro &.first no final do bloco para impedir Nil Exceção
Gagan Gami
12

De acordo com o ruby ​​doc, http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-key key (value) é o método para encontrar a chave na base do valor.

ROLE = {"customer" => 1, "designer" => 2, "admin" => 100}
ROLE.key(2)

retornará o "designer".

Afzal Masood
fonte
6

Dos documentos:

  • (Objeto?) Detectar (ifnone = nil) {| obj | ...}
  • (Objeto?) Find (ifnone = nil) {| obj | ...}
  • (Objeto) detectar (ifnone = nil)
  • (Objeto) find (ifnone = nil)

Passa cada entrada em enum para bloquear. Retorna o primeiro para o qual o bloco não é falso. Se nenhum objeto corresponder, chama ifnone e retorna seu resultado quando especificado, ou retorna nil caso contrário.

Se nenhum bloco for fornecido, um enumerador será retornado.

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

Isso funcionou para mim:

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

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

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

Rimian
fonte
3

A melhor maneira de encontrar a chave para um valor específico é usar o método de chave disponível para um hash ....

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

Espero que resolva o seu problema ...

Aditya Kapoor
fonte
2

Outra abordagem que eu tentaria é usar #map

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

Isso retornará todas as ocorrências de determinado valor. O sublinhado significa que não precisamos que o valor da chave seja transportado para que não seja atribuído a uma variável. A matriz conterá nils se os valores não corresponderem - é por isso que eu coloco #compactno final.

Jared
fonte
1

Aqui está uma maneira fácil de encontrar as chaves de um determinado valor:

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

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

... e para encontrar o valor de uma determinada chave:

    p clients.assoc("orange") 

ele fornecerá o par de valores-chave.

Saleh Rastani
fonte