Coderama
Coderama

Reputation: 11352

How to find a hash key containing a matching value

Given I have the below clients hash, is there a quick ruby way (without having to write a multi-line script) to obtain the key given I want to match the client_id? E.g. How to get the key for client_id == "2180"?

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

Upvotes: 244

Views: 320074

Answers (11)

halfbit
halfbit

Reputation: 3939

We write 2023: (so for all stumbling in here)

find is what you are searching for.

But find returns an array on match or NIL for no match. If we use the argument of find (a proc that is called if no match), and use proc {[]}, we get an empty array on a non-matching find, that fits better to a hash.

people = { 
    ralph: { name: "rafael", … },
    eve: { name: "eveline", … }
    …
    }
    
people[:eve] => {name: "eveline",…} 
people.find { |nick, person| person.name=="rafael" }[1] => { name: "rafael", … }

and

people[:tosca] => nil
people.find { |nick, person| person.name=="toska" }[1] => BANG ([] on nil) 

but

people.find(proc {[]}) { |nick, person| person.name=="toska" }[1] => nil

So if you have an id-like attribute, you can do like that:

person=people[id]
person||=people.find({[]}) { |p| p.nick == id }[1]
person||=people.find({[]}) { |p| p.other_nick == id }[1]

raise error unless person

    
            



    
    

Upvotes: 0

Aillyn
Aillyn

Reputation: 23793

Ruby 1.9 and greater:

hash.key(value) => key

Ruby 1.8:

You could use hash.index

hsh.index(value) => key

Returns the key for a given value. If not found, returns nil.

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

So to get "orange", you could just use:

clients.key({"client_id" => "2180"})

Upvotes: 484

Saleh Rastani
Saleh Rastani

Reputation: 174

Heres an easy way to do find the keys of a given value:

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

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

...and to find the value of a given key:

    p clients.assoc("orange") 

it will give you the key-value pair.

Upvotes: 1

Jared
Jared

Reputation: 816

Another approach I would try is by using #map

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

This will return all occurences of given value. The underscore means that we don't need key's value to be carried around so that way it's not being assigned to a variable. The array will contain nils if the values doesn't match - that's why I put #compact at the end.

Upvotes: 2

Afzal Masood
Afzal Masood

Reputation: 4551

According to ruby doc http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-key key(value) is the method to find the key on the base of value.

ROLE = {"customer" => 1, "designer" => 2, "admin" => 100}
ROLE.key(2)

it will return the "designer".

Upvotes: 14

Aditya Kapoor
Aditya Kapoor

Reputation: 1570

The best way to find the key for a particular value is to use key method that is available for a hash....

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

I hope it solves your problem...

Upvotes: 4

Rimian
Rimian

Reputation: 38418

From the docs:

  • (Object?) detect(ifnone = nil) {|obj| ... }
  • (Object?) find(ifnone = nil) {|obj| ... }
  • (Object) detect(ifnone = nil)
  • (Object) find(ifnone = nil)

Passes each entry in enum to block. Returns the first for which block is not false. If no object matches, calls ifnone and returns its result when it is specified, or returns nil otherwise.

If no block is given, an enumerator is returned instead.

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

This worked for me:

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

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

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

Upvotes: 6

boulder_ruby
boulder_ruby

Reputation: 39695

You could use hashname.key(valuename)

Or, an inversion may be in order. new_hash = hashname.invert will give you a new_hash that lets you do things more traditionally.

Upvotes: 24

Peter DeWeese
Peter DeWeese

Reputation: 18333

You can invert the hash. clients.invert["client_id"=>"2180"] returns "orange"

Upvotes: 51

iNecas
iNecas

Reputation: 1793

try this:

clients.find{|key,value| value["client_id"] == "2178"}.first

Upvotes: 20

Daniel Vandersluis
Daniel Vandersluis

Reputation: 94103

You could use Enumerable#select:

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

Note that the result will be an array of all the matching values, where each is an array of the key and value.

Upvotes: 197

Related Questions