Cómo encontrar una clave hash que contenga un valor coincidente

198

Dado que tengo el siguiente hash de clientes , ¿hay una forma rápida de rubí (sin tener que escribir un script de varias líneas) para obtener la clave dada que quiero que coincida con el id_cliente? Por ejemplo, ¿cómo obtener la clave client_id == "2180"?

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

Respuestas:

177

Puede usar Enumerable # select :

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

Tenga en cuenta que el resultado será una matriz de todos los valores coincidentes, donde cada uno es una matriz de la clave y el valor.

Daniel Vandersluis
fuente
22
@Coderama La diferencia entre findy selectes que finddevuelve la primera coincidencia y select(alias findAll) devuelve todas las coincidencias.
Daniel Vandersluis
Ya veo, así que esta sería la opción más segura para los casos en los que hay más de una coincidencia.
Coderama
1
Esto es mejor que crear un hash completamente nuevo (llamando invert) solo para encontrar un elemento.
Hejazi el
3
Tenga en cuenta que a partir de Ruby 1.9.3 , esto devolverá un nuevo hash con las coincidencias. No devolverá una matriz, como solía hacerlo en Ruby <= 1.8.7 . clients.select{|key, hash| hash["client_id"] == "2180" } # => {"orange"=>{"client_id"=>"2180"}}
AndrewS
Para obtener la (s) clave (s), simplemente pongaclients.select{|key, hash| hash["client_id"] == "2180" }.keys
Mirror318
408

Ruby 1.9 y superior:

hash.key(value) => key

Ruby 1.8 :

Podrías usar hash.index

hsh.index(value) => key

Devuelve la clave para un valor dado. Si no se encuentra, regresa nil.

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

Entonces, para obtener "orange", puedes usar:

clients.key({"client_id" => "2180"})
Aillyn
fuente
2
Esto se volvería un poco desordenado si los hash tuvieran varias claves, porque necesitarías darle todo el hash index.
Daniel Vandersluis
51
Hash#indexes renombrado Hash#keyen Ruby 1.9
Vikrant Chaudhary
12
Tenga en cuenta que esto solo devuelve la primera coincidencia, si hay varias parejas de hash con el mismo valor, devolverá la primera clave con un valor coincidente.
Mike Campbell
47

Puedes invertir el hash. clients.invert["client_id"=>"2180"]devoluciones"orange"

Peter DeWeese
fuente
3
¡Esto también parece una forma inteligente (porque es corta) de hacerlo!
Coderama
esto es lo que necesito para las matrices de formularios (para cuadros seleccionados) que crean un hash hacia atrás
Joseph Le Brech
22

Podría usar hashname.key (valuename)

O, una inversión puede estar en orden. new_hash = hashname.invertte dará una new_hashque te permite hacer las cosas más tradicionalmente.

boulder_ruby
fuente
3
Esta es la forma correcta de hacerlo en versiones recientes (1.9+) de Ruby.
Lars Haugseth
#invertes una muy mala idea en este caso, ya que esencialmente está asignando memoria para objetos hash desechables solo por el hecho de encontrar una clave. Dependiendo del tamaño del hash, tiene un serio impacto en el rendimiento
Dr.Strangelove
15

prueba esto:

clients.find{|key,value| value["client_id"] == "2178"}.first
iNecas
fuente
44
Esto arrojará una excepción si find devuelve nil, porque no puede llamar a .first on nil.
Schrockwell
1
Si usa Rails, puede usarlo en .try(:first)lugar de .firstevitar excepciones (si espera que sea posible que falte el valor).
codificación aaron
2
en Ruby 2.3.0 +puede usar un navegador seguro &.first al final del bloque para evitar una excepción
nula
6

De los documentos:

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

Pasa cada entrada en la enumeración para bloquear. Devuelve el primero para el que el bloque no es falso. Si ningún objeto coincide, llama a ifnone y devuelve su resultado cuando se especifica, o devuelve nil de lo contrario.

Si no se proporciona ningún bloque, se devuelve un enumerador.

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

Esto funcionó para mí:

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

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

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

Rimian
fuente
3

La mejor manera de encontrar la clave para un valor particular es utilizar el método de clave que está disponible para un hash ...

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

Espero que resuelva tu problema ...

Aditya Kapoor
fuente
2

Otro enfoque que intentaría es usar #map

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

Esto devolverá todas las ocurrencias de un valor dado. El guión bajo significa que no necesitamos que el valor de la clave se transfiera de modo que no se asigne a una variable. La matriz contendrá nils si los valores no coinciden, es por eso que pongo #compactal final.

Jared
fuente
1

Aquí hay una manera fácil de encontrar las claves de un valor dado:

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

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

... y para encontrar el valor de una clave dada:

    p clients.assoc("orange") 

te dará el par clave-valor.

Saleh Rastani
fuente