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.