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"}
}

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.


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"})

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"


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.


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

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


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


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 ...


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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.