Jak znaleźć klucz skrótu zawierający pasującą wartość

198

Biorąc pod uwagę, że mam poniższy skrót klientów , czy istnieje szybki rubinowy sposób (bez konieczności pisania skryptu składającego się z wielu wierszy), aby uzyskać klucz, biorąc pod uwagę, że chcę dopasować identyfikator klienta? Np. Jak zdobyć klucz client_id == "2180"?

clients = {
  "yellow"=>{"client_id"=>"2178"}, 
  "orange"=>{"client_id"=>"2180"}, 
  "red"=>{"client_id"=>"2179"}, 
  "blue"=>{"client_id"=>"2181"}
}
Coderama
źródło

Odpowiedzi:

176

Możesz użyć Enumerable # select :

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

Zauważ, że wynikiem będzie tablica wszystkich pasujących wartości, gdzie każda jest tablicą klucza i wartości.

Daniel Vandersluis
źródło
22
@Coderama Różnica między findi selectpolega na tym, że findzwraca pierwsze dopasowanie, a select(które jest aliasowane findAll) zwraca wszystkie dopasowania.
Daniel Vandersluis,
Rozumiem, więc byłaby to bezpieczniejsza opcja dla przypadków, w których występuje więcej niż jedno dopasowanie.
Coderama,
1
Jest to lepsze niż tworzenie całego nowego skrótu (przez dzwonienie invert) tylko w celu znalezienia elementu.
Hejazi
3
Zauważ, że od Ruby 1.9.3 , to zwróci nowy skrót z dopasowaniami. Nie zwróci tablicy, jak kiedyś w Ruby <= 1.8.7 . clients.select{|key, hash| hash["client_id"] == "2180" } # => {"orange"=>{"client_id"=>"2180"}}
AndrewS,
Aby uzyskać klucz (klucze), po prostu włóżclients.select{|key, hash| hash["client_id"] == "2180" }.keys
Mirror318
407

Ruby 1.9 i nowszy:

hash.key(value) => key

Ruby 1.8 :

Możesz użyć hash.index

hsh.index(value) => key

Zwraca klucz dla danej wartości. Jeśli nie znaleziono, zwraca nil.

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

Aby uzyskać "orange", możesz po prostu użyć:

clients.key({"client_id" => "2180"})
Aillyn
źródło
2
Byłoby to trochę niechlujne, gdyby skróty miały wiele kluczy, ponieważ musiałbyś oddać cały skrót index.
Daniel Vandersluis,
51
Hash#indexprzemianowano na Hash#keyRuby 1.9
Vikrant Chaudhary
12
Zauważ, że to zwraca tylko pierwsze dopasowanie, jeśli istnieje wiele par skrótów o tej samej wartości, zwróci pierwszy klucz z pasującą wartością.
Mike Campbell
47

Możesz odwrócić skrót. clients.invert["client_id"=>"2180"]zwroty"orange"

Peter DeWeese
źródło
3
Wydaje się to również sprytnym sposobem (ponieważ jest krótki), aby to zrobić!
Coderama,
tego właśnie potrzebuję dla tablic formularzy (dla wybranych pól), które tworzą hash wsteczny
Joseph Le Brech
22

Możesz użyć hashname.key (nazwa wartości)

Lub odwrócenie może być w porządku. new_hash = hashname.invertda ci coś, new_hashco pozwala robić rzeczy bardziej tradycyjnie.

boulder_ruby
źródło
3
Jest to właściwy sposób, aby to zrobić w najnowszych wersjach Ruby (1.9+).
Lars Haugseth,
#invertjest naprawdę złym pomysłem w tym przypadku, ponieważ w zasadzie alokujesz pamięć dla wyrzucanego obiektu mieszającego tylko ze względu na znalezienie klucza. W zależności od wielkości skrótu ma to poważny wpływ na wydajność
Dr.Strangelove
15

Spróbuj tego:

clients.find{|key,value| value["client_id"] == "2178"}.first
iNecas
źródło
4
Spowoduje to zgłoszenie wyjątku, jeśli find zwróci zero, ponieważ nie można wywołać .first na zero.
Schrockwell,
1
Jeśli używasz Railsów, możesz użyć .try(:first)zamiast .firstunikać wyjątków (jeśli spodziewasz się, że będzie możliwe, że brakuje wartości).
kodowanie aaron
2
w Ruby 2.3.0 +możesz użyć bezpiecznego nawigatora &.first na końcu bloku, aby zapobiec zerowemu wyjątkowi
Gagan Gami,
6

Z dokumentów:

  • (Obiekt?) Wykryj (ifnone = zero) {| obj | ...}
  • (Obiekt?) Znajdź (ifnone = zero) {| obj | ...}
  • Wykrywanie (obiektu) (ifnone = zero)
  • (Obiekt) znajdź (ifnone = zero)

Przechodzi każdy wpis w enum do zablokowania. Zwraca pierwszy, dla którego blok nie jest fałszywy. Jeśli żaden obiekt nie pasuje, wywołuje ifnone i zwraca wynik, gdy jest określony, lub zwraca zero w przeciwnym razie.

Jeśli nie podano żadnego bloku, zamiast tego zwracany jest moduł wyliczający.

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

To działało dla mnie:

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

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

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

Rimian
źródło
3

Najlepszym sposobem znalezienia klucza dla określonej wartości jest użycie metody klucza dostępnej dla skrótu ....

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

Mam nadzieję, że to rozwiąże twój problem ...

Aditya Kapoor
źródło
2

Innym podejściem, którego spróbowałbym, jest użycie #map

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

Zwróci to wszystkie wystąpienia o podanej wartości. Podkreślenie oznacza, że ​​nie potrzebujemy przenosić wartości klucza, aby w ten sposób nie był przypisany do zmiennej. Tablica będzie zawierać zero, jeśli wartości się nie zgadzają - dlatego umieszczam #compactna końcu.

Jared
źródło
1

Oto prosty sposób na znalezienie kluczy o danej wartości:

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

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

... i znaleźć wartość danego klucza:

    p clients.assoc("orange") 

da ci parę klucz-wartość.

Saleh Rastani
źródło