return an array of keys from hash when values match pattern - ruby

I am trying to run through the following hash
my_family_pets_ages = {"Evi" => 6, "Hoobie" => 3, "George" => 12, "Bogart" => 4, "Poly" => 4, "Annabelle" => 0, "Ditto" => 3}
and return an array of the keys whose values match a specified integer for age. So, for example, if I wanted to find all of the pets that are 3 years old, it would return an array of just their names.
["Hoobie", "Ditto"]
I have the following method, but I can't seem to get the method to return an array of just the keys, but I keep just getting the key => value in an array like this:
["Hoobie"=>3, "Ditto"=>3]
Here is the method I have so far
def my_hash_finding_method(source, thing_to_find)
source.select {|name, age| name if age == thing_to_find}
end
Any pointers? I'm stuck on how to return only the keys

Just use #select, then #keys to get an array of the matching keys:
def my_hash_finding_method(source, thing_to_find)
source.select { |name, age| age == thing_to_find }.keys
end
See Hash#keys for more information.

Related

Filtering when adding elements by id

Tell me how to do this, where you can read about it, because I do not understand at all how to implement it. Thanks.
def initialize
#arr = []
end
def items(init)
arrfinish = init - #arr
#arr = (#arr + init).uniq
yield arrfinish
end
def idefine(find_text)
end
The class has a method(items) that connects arrays by removing duplicate elements. I need to make sure that the idefine
method receives the key by which filtering will be performed when adding new elements, I will give an example below.
app_handler.idefine('id')
app_handler.items([{'id' => 1}, {'id' => 1, 'test_key' => 'Some data'}, {'id' => 2}])
From this example, the second element with id = 1 should be ignored.
Ok, putting aside the class definition, what I understand of the question is:
In an array of hashes, remove the hashes that contain a duplicate value of a given key.
The following function filter the hashes and copy the content of the selected ones in a new array:
require 'set'
def no_dup_val key, arr
previous_values = Set[]
arr.each_with_object([]) do |hash,result|
next unless hash.has_key?(key)
next if previous_values.include?(hash[key])
previous_values << hash[key]
result << hash.dup
end
end
Which gives you:
no_dup_val 'id', [{'id' => 1}, {'id' => 1, 'key' => 'data'}, {'id' => 2}, {'stock' => 3}, {'e-stock'=>0}]
#=> [{"id"=>1}, {"id"=>2}]
Note that the hashes that don't contain the key are also removed, that's my choice, which leads to the following questions:
What happens when the key is not present in a hash?
What happens when the item function is called more than once? Do you take into account the hashes already in #arr?
What happens when you call idefine with a new key? Do you filter the existing elements of #arr with the new key?
As you can see, you need to be a little more specific about what you want to do.
Update
If you don't care about copying the contents of the hashes then these may fit your needs.
Hashes without the id key are removed:
def no_dup_val key, arr
arr.filter{ |h| h.has_key?(key) }.uniq{ |h| h[key] }
end
no_dup_val 'id', [{'id' => 1}, {'id' => 1, 'key' => 'data'}, {'id' => 2}, {'stock' => 3}, {'e-stock'=>0}]
#=> [{"id"=>1}, {"id"=>2}]
Hashes without the id key are treated as having "id" => nil (so the first will be kept):
def no_dup_val key, arr
arr.uniq{ |h| h[key] }
end
no_dup_val 'id', [{'id' => 1}, {'id' => 1, 'key' => 'data'}, {'id' => 2}, {'stock' => 3}, {'e-stock'=>0}]
#=> [{"id"=>1}, {"id"=>2}, {"stock"=>3}]
All the hashes without the id key are kept:

I need to convert a string to an integer within a hash

I have a nested hash and I need to return the inside hash, which is a value of the key. The problem is one of the values is a string and it needs to be returned as an integer.
def player_stats(player_name)
game_hash.keys.each do |data|
if game_hash[data][:players].keys.include?(player_name)
return game_hash[data][:players][player_name]
end
end
end
game_hash = {
:home => {team_name:"Brooklyn Nets", colors:["Black", "White"],
:players => {"Alan Anderson" => {:number => 0, :shoe => "16",
:points => 22, :rebounds => 12, :assists =>12, :steals => 3,
:blocks => 1, :slam_dunks => 1},
The code is correct, the only thing is that I need to input a line that will convert the string**(:shoe)** to an integer.
game_hash = {
:home => {:team_name =>"Brooklyn Nets",
:colors => ["Black", "White"],
:players => {
"Alan Anderson" => {
:number => 0,
:shoe => "16",
:points => 22,
}
}
}
}
In this case, assuming you do not want to mutate (modify) the original hash, it's easiest to first make a deep copy of game_hash:
h = Marshal.load(Marshal.dump(game_hash))
#=> {:home=>{:team_name=>"Brooklyn Nets", :colors=>["Black", "White"],
# :players=>{"Alan Anderson"=>{:number=>0, :shoe=>"16", :points=>22}}}}
See Marshall::load and Marshall::dump.
Then just modify the value of interest:
h[:home][:players]["Alan Anderson"][:shoe] =
h[:home][:players]["Alan Anderson"][:shoe].to_i
h #=> {:home=>{:team_name=>"Brooklyn Nets", :colors=>["Black", "White"],
# :players=>{"Alan Anderson"=>{:number=>0, :shoe=>16, :points=>22}}}}
Lastly, let's confirm the original hash was not mutated:
game_hash
#=> {:home=>{:team_name=>"Brooklyn Nets", :colors=>["Black", "White"],
# :players=>{"Alan Anderson"=>{:number=>0, :shoe=>"16", :points=>22}}}}
Following your code, just a couple of changes (see inline comments in the option below).
Given the game_hash, which could have integers as string somewhere:
game_hash = {:home => { team_name:"Brooklyn Nets", colors:["Black", "White"],:players => {"Alan Anderson" => {:number => "0", :shoe => "16", :points => 22, :rebounds => 12, :assists =>12, :steals => 3, :blocks => 1, :slam_dunks => 1},"Reggie Evans" => {:number => "30",:shoe => "14",:points => 12, :rebounds => 12, :assists => 12, :steals => 12, :blocks => 12, :slam_dunks => 7}}}}
This is the option:
def player_stats(player_name, game_hash) # <-- pass game_hash as parameter
game_hash.keys.each do |data|
if game_hash[data][:players].keys.include?(player_name)
return game_hash[data][:players][player_name].transform_values { |v| v.to_i } # <-- transform values to integer
end
end
end
Then, you can call:
player_stats("Reggie Evans", game_hash)
#=> {:number=>30, :shoe=>14, :points=>12, :rebounds=>12, :assists=>12, :steals=>12, :blocks=>12, :slam_dunks=>7}
There are a few "issues" that I would like to point out in your #player_stats method, but I will focus on your problem with converting the player hash values to integers. To start off, I am making the following assumptions:
There is no consistency in whether the values are formatted as a string or integer.
All values are of integer form, in either the Integer type, or String type.
One thing I would like to point out is that shoe size could be 9.5 or 10.5, etc. This could be the reason why the shoe value is formatted as a string. If this is the case, then you would need to take this into account and convert to a float using #to_f instead of #to_i. But since your question is asking for it to be an integer, I will use #to_i in my examples below.
Given my assumptions, you would simply loop over all the values in the player hash and call #to_i on the value to convert it to an integer. This can be done using the following method:
def convert_hash_values_to_int(hash)
hash.each do |key, value|
hash[key] = value.to_i
end
end
You can call this method inside your #player_stats method. Note that this method is mutating the original hash values in place without creating a new hash. If this is not desirable, then you should use #inject:
def convert_hash_values_to_int(hash)
hash.inject({}) do |result, (key, value)|
result[key] = value.to_i
result
end
end
This creates a new hash with the transformed values, and returns it.
This can be simplified even further using #each_with_object, which also creates a new hash instead of mutating the original hash:
def convert_hash_values_to_int(hash)
hash.each_with_object({}) do |(key, value), result|
result[key] = value.to_i
end
end
This approach does not require that you return result in each loop. Note that the arguments are switched for #each_with_object - |(key, value), result| instead of |result, (key, value)|.
There is one more approach that you could use which is the most succinct, but it is only available in Ruby 2.4+:
def convert_hash_values_to_int(hash)
hash.transform_values do |value|
value.to_i
end
end
Which can be even more succinct:
def convert_hash_values_to_int(hash)
hash.transform_values(&:to_i)
end
#transform_values does not mutate the original hash. If you would like to mutate the original hash, you would need to use a bang:
def convert_hash_values_to_int(hash)
hash.transform_values!(&:to_i)
end

Retrieving values from corresponding keys in Ruby hash

The following only prints white lines. If this isn't the way to retrieve values from keys in Ruby, what is?
numbers = []
for i in 1..100 do
hash = {
:FizzBuzz => 1,
:Prime => 3,
:Fibonacci => 5
}
numbers << { i => hash }
end
numbers.each do |number|
puts number[:Prime]
end
Note this is a MCVE, in the final application 1, 3 and 5 will be function calls.
Try it
For those wondering what I was trying to do, the final (non-MCVE) result can be found on Code Review.
After the first loop, numbers is an array like this:
[
{ 1 => { :FizzBuzz => 1, :Prime => 3, :Fibonacci => 5 } },
{ 2 => { :FizzBuzz => 1, :Prime => 3, :Fibonacci => 5 } },
...
{ 100 => { :FizzBuzz => 1, :Prime => 3, :Fibonacci => 5 } }
]
number[:Prime] is trying to fetch the :Prime element out of your first array element, but you only have the key 1.
The end result is, you print empty lines, as nothing is found at each iteration. Hash access is okay; it is the logic of your code that is the problem (and you did not explain what you are trying to do precisely).
Each number is a hash of this form:
{1=>{:FizzBuzz=>1, :Prime=>3, :Fibonacci=>5}}
which has a number as the sole key. When you look for a hash key that does not exist using Hash#[], Ruby returns nil. And puts nil prints a blank line.
Indeed Hash#[] is the way to retrieve values from a hash.
The keys of number are all integers (coming from i). The symbol :Prime is not a key. Therefore the results are all nil.

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

How can I assign multiple values to a hash key?

For the sake of convenience I am trying to assign multiple values to a hash key in Ruby. Here's the code so far
myhash = { :name => ["Tom" , "Dick" , "Harry"] }
Looping through the hash gives a concatenated string of the 3 values
Output:
name : TomDickHarry
Required Output:
:name => "Tom" , :name => "Dick" , :name => "Harry"
What code must I write to get the required output?
myhash.each_pair {|k,v| v.each {|n| puts "#{k} => #{n}"}}
#name => Tom
#name => Dick
#name => Harry
The output format is not exactly what you need, but I think you get the idea.
The answers from Rohith and pierr are fine in this case. However, if this is something you're going to make extensive use of it's worth knowing that the data structure which behaves like a Hash but allows multiple values for a key is usually referred to as a multimap. There are a couple of implementations of this for Ruby including this one.
You've created a hash with the symbol name as the key and an array with three elements as the value, so you'll need to iterate through myhash[:name] to get the individual array elements.
re: the issue of iterating over selective keys. Try using reject with the condition inverted instead of using select.
e.g. given:
{:name=>["Tom", "Dick", "Harry"], :keep=>[4, 5, 6], :discard=>[1, 2, 3]}
where we want :name and :keep but not :discard
with select:
myhash.select { |k, v| [:name, :keep].include?(k) }
=> [[:name, ["Tom", "Dick", "Harry"]], [:keep, [4, 5, 6]]]
The result is a list of pairs.
but with reject:
myhash.reject { |k, v| ![:name, :keep].include?(k) }
=> {:name=>["Tom", "Dick", "Harry"], :keep=>[4, 5, 6]}
The result is a Hash with only the entries you want.
This can then be combined with pierr's answer:
hash_to_use = myhash.reject { |k, v| ![:name, :keep].include?(k) }
hash_to_use.each_pair {|k,v| v.each {|n| puts "#{k} => #{n}"}}

Resources