How to find a hash key containing a matching value - ruby

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

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

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.

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

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.

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

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

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

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

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.

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.

Related

Detecting if a key-value pair exists within a hash

I cannot find a way to determine if a key-value pair exists in a hash.
h4 = { "a" => 1, "d" => 2, "f" => 35 }
I can use Hash#has_value? and Hash#has_key? to find information about a key or a value individually, but how can I check if a pair exists?
Psuedo-code of what I'm after:
if h4.contains_pair?("a", 1)
Just use this:
h4['a'] == 1
It seems excessive to me, but you could monkey-patch Hash with a method like so:
class Hash
def contains_pair?(key, value)
key?(key) && self[key] == value
end
end
I confess to starting down a road and then wondering where it might take me. This may not be the best way of determining if a key/value pair is present in a hash (how could one improve on #Jordan's answer?), but I learned something along the way.
Code
def pair_present?(h,k,v)
Enumerable.instance_method(:include?).bind(h).call([k,v])
end
Examples
h = { "a"=>1, "d"=>2, "f"=>35 }
pair_present?(h,'a',1)
#=> true
pair_present?(h,'f',36)
#=> false
pair_present?(h,'hippopotamus',2)
#=> false
Discussion
We could of course convert the hash to an array and then apply Array#include?:
h.to_a.include?(['a', 1])
#=> true
but that has the downside of creating a temporary array. It would be nice if the class Hash had such an instance method, but it does not. One might think Hash#include? might be used for that, but it just takes one argument, a key.1.
The method Enumerable#include? does what we want, and of course Hash includes the Enumerable module. We can invoke that method in various ways.
Bind Enumerable#include? to the hash and call it
This was of course my answer:
Enumerable.instance_method(:include?).bind(h).call([k,v])
Use the method Method#super_method, which was introduced in v2.2.
h.method(:include?).super_method.call(['a',1])
#=> true
h.method(:include?).super_method.call(['a',2])
#=> false
Note that:
h.method(:include?).super_method
#=> #<Method: Object(Enumerable)#include?>
Do the alias_method/remove_method merry-go-round
Hash.send(:alias_method, :temp, :include?)
Hash.send(:remove_method, :include?)
h.include?(['a',1])
#=> true
h.include?(['a',2])
#=> false
Hash.send(:alias_method, :include?, :temp)
Hash.send(:remove_method, :temp)
Convert the hash to an enumerator and invoke Enumerable#include?
h.to_enum.include?(['a',1])
#=> true
h.to_enum.include?(['a',2])
#=> false
This works because the class Enumerator also includes Enumerable.
1 Hash#include? is the same as both Hash#key? and Hash#has_key?. It makes me wonder why include? isn't used for the present purpose, since determining if a hash has a given key is well-covered.
How about using Enumerable any?
h4 = { "a" => 1, "d" => 2, "f" => 35 }
h4.any? {|k,v| k == 'a' && v == 1 }

Count array object types in one iteration

I have a JSON with format
{body => ["type"=>"user"...], ["type"=>"admin"...]}
I want to count the objects by type, but I don't want to iterate the array three times (that's how many different objects I have), so this won't work:
#user_count = json["body"].count{|a| a['type'] == "user"}
#admin_count = json["body"].count{|a| a['type'] == "admin"}
...
Is there a smart way to count the object types without doing an .each block and using if statements?
You could use each_with_object to create a hash with type => count pairs:
json['body'].each_with_object(Hash.new(0)) { |a, h| h[a['type']] += 1 }
#=> {"user"=>5, "admin"=>7, ...}
You can store counts into a hash using one each:
counts = { "user" => 0, "admin" => 0, "whatever" => 0 }
json["body"].each do |a|
counts[a.type] += 1
end
counts["user"] #=> 1
counts["admin"] #=> 2
counts["whatever"] #=> 3

Fetch key values directly from a custom hash format

I have a hash as shown below:
hash_ex = {:"p, q," => "1, 2,"}
And hash_ex[:p] returns nil:
hash_ex[:p]
# => nil
Instead, it should be 1. How would I be able to get this key value?
There is no defined key :p in hash_ex. Therefore nil is expected result.
hash_ex = {:"p, q,"=>"1, 2,"}
hash_ex.keys
# => [:"p, q,"]
hash_ex[:"p, q,"]
# => "1, 2,"
The above example shows that you have declared "p, q," as a key. Also "p, q," here is being treated as a Symbol and not a String. Therefore hash_ex["p, q,"] will also return nil.
But hash_ex[:p] returns nil instead it should be 1
Ruby is not an AI, it can't figure out how to parse your "custom Hash format".
How I would be able to get this key value?
Well, you have to convert your format into a structure that Ruby understands:
key_string = "p, q,"
value_string = "1, 2,"
keys = key_string.split(/,\s*/).map(&:to_sym) #=> [:p, :q]
values = value_string.split(/,\s*/).map(&:to_i) #=> [1, 2]
hash_ex = keys.zip(values).to_h
hash_ex[:p] #=> 1

What is meant: "Hash.new takes a default value for the hash, which is the value of the hash for a nonexistent key"

I'm currently going through the Ruby on Rails tutorial by Michael Hartl
Not understanding the meaning of this statement found in section 4.4.1:
Hashes, in contrast, are different. While the array constructor
Array.new takes an initial value for the array, Hash.new takes a
default value for the hash, which is the value of the hash for a
nonexistent key:
Could someone help explain what is meant by this? I don't understand what the author is trying to get at regarding how hashes differ from arrays in the context of this section of the book
You can always try out the code in irb or rails console to find out what they mean.
Array.new
# => []
Array.new(7)
# => [nil, nil, nil, nil, nil, nil, nil]
h1 = Hash.new
h1['abc']
# => nil
h2 = Hash.new(7)
h2['abc']
# => 7
Arrays and hashes both have a constructor method that takes a value. What this value is used for is different between the two.
For arrays, the value is used to initialize the array (example taken from mentioned tutorial):
a = Array.new([1, 3, 2])
# `a` is equal to [1, 3, 2]
Unlike arrays, the new constructor for hashes doesn't use its passed arguments to initialize the hash. So, for example, typing h = Hash.new('a', 1) does not initialize the hash with a (key, value) pair of a and 1:
h = Hash.new('a', 1) # NO. Does not give you { 'a' => 1 }!
Instead, passing a value to Hash.new causes the hash to use that value as a default when a non-existent key is passed. Normally, hashes return nil for non-existent keys, but by passing a default value, you can have hashes return the default in those cases:
nilHash = { 'x' => 5 }
nilHash['x'] # Return 5, because the key 'x' exists in nilHash
nilHash['foo'] # Returns nil, because there is no key 'foo' in nilHash
defaultHash = Hash.new(100)
defaultHash['x'] = 5
defaultHash['x'] # Return 5, because the key 'x' exists in defaultHash
defaultHash['foo']
# Returns 100 instead of nil, because you passed 100
# as the default value for non-existent keys for this hash
Begin by reading the docs for the class method Hash#new. You will see there are three forms:
new → new_hash
new(obj) → new_hash
new {|hash, key| block } → new_hash
Creating an Empty Hash
The first form is used to create an empty hash:
h = Hash.new #=> {}
which is more commonly written:
h = {} #=> {}
The other two ways of creating a hash with Hash#new establish a default value for a key/value pair when the hash does not already contain the key.
Hash.new with an argument
You can create a hash with a default value in one of two ways:
Hash.new(<default value>)
or
h = Hash.new # or h = {}
h.default = <default value>
Suppose the default value for the hash were 4; that is:
h = Hash.new(4) #=> {}
h[:pop] = 7 #=> 7
h[:pop] += 1 #=> 8
h[:pop] #=> 8
h #=> {:pop=>8}
h[:chips] #=> 4
h #=> {:pop=>8}
h[:chips] += 1 #=> 5
h #=> {:pop=>8, :chips=>5}
h[:chips] #=> 5
Notice that the default value does not affect the value of :pop. That's because it was created with an assignment:
h[:pop] = 7
h[:chips] by itself merely returns the default value (4); it does not add the key/value pair :chips=>4 to the hash! I repeat: it does not add the key/value pair to the hash. That's important!
h[:chips] += 1
is shorthand for:
h[:chips] = h[:chips] + 1
Since the hash h does not have a key :chips when h[:chips] on the right side of the equals sign is evaluated, it returns the default value of 4, then 1 is added to make it 5 and that value is assigned to h[:chips], which adds the key value pair :chips=>5 to the hash, as seen in following line. The last line merely reports the value for the existing key :chips.
So why would you want to establish a default value? I would venture that the main reason is to be able to initialize it with zero, so you can use:
h[k] += 1
instead of
k[k] = (h.key?(k)) ? h[k] + 1 : 1
or the trick:
h[k] = (h[k] ||= 0) + 1
(which only works when hash values are intended to be non-nil). Incidentally, key? is aka has_key?.
Can we make the default a string instead? Of course:
h = Hash.new('magpie')
h[:bluebird] #=> "magpie"
h #=> {}
h[:bluebird] = h[:bluebird] #=> "magpie"
h #=> {:bluebird=>"magpie"}
h[:redbird] = h[:redbird] #=> "magpie"
h #=> {:bluebird=>"magpie", :redbird=>"magpie"}
h[:bluebird] << "jay" #=> "magpiejay"
h #=> {:bluebird=>"magpiejay", :redbird=>"magpiejay"}
You may be scratching your head over the last line: why did h[:bluebird] << "jay" cause h[:redbird] to change?? Perhaps this will explain what's going on here:
h[:robin] #=> "magpiejay"
h[:robin].object_id #=> 2156227520
h[:bluebird].object_id #=> 2156227520
h[:redbird].object_id #=> 2156227520
h[:robin] merely returns the default value, which we see has been changed from "magpie" to "magpiejay". Now look at the object_id's for the default value and for the values associated with the keys :bluebird and :redbird. As you see, all values are the same object, so if we change one, we change all the the others, including the default value. It is now evident why h[:bluebird] << "jay" changed the default value.
We can clarify this further by adding a stately eagle:
h[:eagle] #=> "magpiejay"
h[:eagle] += "starling" #=> "magpiejaystarling"
h[:eagle].object_id #=> 2157098780
h #=> {:bluebird=>"magpiejay", :redbird=>"magpiejay", :eagle=>"magpiejaystarling"}
Because
h[:eagle] += "starling" #=> "magpiejaystarling"
is equivalent to:
h[:eagle] = h[:eagle] + "starling"
we have created a new object on the right side of the equals sign and assigned it to h[:eagle]. That's why the values for the keys :bluebird and :redbird are unaffected and h[:eagle] has a different object_id.
We have the similar problems if we write: Hash.new([]) or Hash.new({}). If there are ever reasons to use those defaults, I'm not aware of them. It certainly can be very useful for the default value to be an empty string, array or hash, but for that you need the third form of Hash.new, which takes a block.
Hash.new with a block
We now consider the third and final version of Hash#new, which takes a block, like so:
Hash.new { |h,k| ??? }
You may be expecting this to be devilishly complex and subtle, certainly much harder to grasp than the other two forms of the method. If so, you'd be wrong. It's actually quite simple, if you think of it as looking like this:
Hash.new { |h,k| h[k] = ??? }
In other words, Ruby is saying to you, "The hash h doesn't have the key k. What would you like it's value to be? Now consider the following:
h7 = Hash.new { |h,k| h[k]=7 }
hs = Hash.new { |h,k| h[k]='cat' }
ha = Hash.new { |h,k| h[k]=[] }
hh = Hash.new { |h,k| h[k]={} }
h7[:a] += 3 #=> 10
hs[:b] << 'nip' #=> "catnip"
ha[:c] << 4 << 6 #=> [4, 6]
ha[:d] << 7 #=> [7]
ha #=> {:c=>[4, 6], :d=>[7]}
hh[:k].merge({b: 4}) #=> {:b=>4}
hh #=> {}
hh[:k].merge!({b: 4} ) #=> {:b=>4}
hh #=> {:k=>{:b=>4}}
Notice that you cannot write ha = Hash.new { |h,k| [] } (or equivalently, ha = Hash.new { [] }) and expect h[k] => [] to be added to the hash. You can do whatever you like within the block; you are neither required nor limited to specifying a value for the key. In effect, within the block Ruby is actually saying, "A key that is not in the hash has been referenced without a value. I'm giving you that reference and also a reference to the hash. That will allow you to add that key with a value to the hash, if that's what you want to do, but what you do in this block is entirely your business."
The default values for the hashes h7, hs, ha and hh are respectively the number 7 (though it would be easier to simply enter 7 as An argument), an empty string, an empty array or an empty hash. Probably the last two are the most common use of Hash#new with a block, as in:
array = [[:a, 1], [:b, 3], [:a, 4], [:b, 6]]
array.each_with_object(Hash.new {|h,k| h[k] = []}) { |(k,v),h| h[k] << v }
#=> {:a=>[1, 4], :b=>[3, 6]}
That's really about all there is to the last form of Hash#new.

How to get the first key and value pair from a hash table in Ruby

I'm trying to get the first key and value key from a hash table in ruby. I don't know the key values of the hash because it is passed to the method. I cant find anywhere online how to find the first key/value as a separate hash table.
I think hash[0] will just try to find an element with a name 0 it just returns nil when I run the code.
I know I can find the key name and the value and then create a new hash from them but i wonder if there is an easier way to do this so I get a hash right away.
here is my code:
def rps_game_winner(game)
rock_in_hash = game.invert['R']
paper_in_hash = game.invert['P']
scissors_in_hash = game.invert['S']
if(rock_in_hash)
if(paper_in_hash)
return paper_in_hash;
elsif(scissors_in_hash)
return rock_in_hash
end
elsif(paper_in_hash)
if(rock_in_hash)
return paper_in_hash
elsif(scissors_in_hash)
return scissors_in_hash
end
end
key = game.keys[-1]
value = game.values[-1]
winner = {key => value}
return winner
end
game_one = { "Bob" => 'P', "Jim" => 'P' }
puts rps_game_winner(game_one)
This gets me the correct result the problem is I don't understand why it's -1 instead of zero...
And i was hoping there was a better way to get the first key/value pair of a hash table instead of creating new hash table with the key and value you retrieved from the previous table.
You can just do
key, value = hash.first
or if you prefer:
key = hash.keys[0]
value = hash.values[0]
Then maybe:
new_hash = {key => value}
There is a shorter answer that does not require you to use extra variables:
h = { "a" => 100, "b" => 200 , "c" => 300, "d" => 400, "e" => 500}
Hash[*h.first] #=> {"a" => 100}
Or if you want to retrieve a key/value at a any single position
Hash[*h.to_a.at(1)] #=> {"b" => 200}
Or retrieve a key/values from a range of positions:
Hash[h.to_a[1,3]] #=> {"b"=>200, "c"=>300, "d"=>400}
[hash.first].to_h
Another way to do it.
my_hash = { "a" => "first", "b" => "second" }
{ my_hash.keys.first => my_hash.values.first }
This works too

Resources