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

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.


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

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"


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.


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

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


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


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.


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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.