Ruby hash with key as hash and value as array - ruby

I am creating a hash, whose key is a hash and the value is an array. E.g.,
shop = Hash.new
items.each do |item|
grouping_key = {
'name'=> item['name'],
'value'=> item['value']
}
shop [grouping_key] ||= Array.new
shop [grouping_key] << item
end
Here, I am grouping each item based on grouping key. For the following items:
Item1 = {'name'=>'test', 'value'=>10, 'color'=>'black', 'description'=>'item1'}
Item2 = {'name'=>'test2', 'value'=>10, 'color'=>'blue', 'description'=>'item2'}
Item3 = {'name'=>'test', 'value'=>10, 'color'=>'black', 'description'=>'item3'}
Item4 = {'name'=>'test2', 'value'=>10, 'color'=>'blue', 'description'=>'item4'}
my shop hash will be:
shop = {{'name'=>'test', 'value'=>10}=>[Item1, Item3], {name=>test2, value=>10}=>[Item2, Item4]}
I wanted to add color to hash key, but not as part of grouping key. Is it possible to do so without reiterating over hash and modifying it? e.g.
shop = {{'name'=>'test', 'value'=>10, 'color'=>'black'}=>[Item1, Item3], {'name'=>'test2', 'value'=>10, 'color'=>'blue'}=>[Item2, Item4]}
Any other approach will also be helpful.

Your initial code is equivalent to
shop = items.group_by do | i |
{'name' => i['name'], 'value' => i['value'] }
end
To add the color to the key hash, simply do
shop = items.group_by do | i |
{'name' => i['name'], 'value' => i['value'], 'color' => i['color'] }
end
Now, you are grouping by color too.
If this is not your intention ("but not as part of grouping key"), i.e. if there can be items with the same name and value but different color, and these items shall go into the same group, then you first have to decide which color should be in the group's hash then.
In that case, postprocessing the hash would be simplest:
shop = items.group_by do | i |
{'name' => i['name'], 'value' => i['value'] }
end
shop.keys.each { | h | h['color'] = shop[h].sample['color'] }

Related

Next key/value pair overwrites the existing pair in a hash while trying to add pair with new key

I have:
fruits = {
"orange" => {:season => "winter"},
"apple" => {:season => "winter"},
"banana" => {:season => "summer"},
"grape" => {:season => "spring"},
"peach" => {:season => "winter"},
"pineapple" => {:season => "summer"}
}
I want to get:
{
"winter"=>["orange", "apple", "peach"],
"summer"=>["banana", "pineapple"],
"spring"=>["grape"]
}
I did:
def sort_fruits(fruits_hash)
fruits=[]
sorted = {}
seasons = fruits_hash.map {|k, v|v[:season]}
seasons.uniq.each do |season|
fruits.clear
fruits_hash.each do |fruit, season_name|
if season == season_name[:season]
fruits << fruit
end
end
p sorted[season] = fruits ## season changes to new season, so this should have created new key/value pair for new season.
end
sorted
end
I get:
{
"winter"=>["grape"],
"summer"=>["grape"],
"spring"=>["grape"]
}
I couldn't figure out why adding new key/value pair with unique key would overwrite existing pair in a hash. Any help with explanation would be greatly appreciated.
In Ruby mutable objects are passed by reference. It means that when you iterate over seasons in each block this line:
sorted[season] = fruits
saves to sorted[season] a reference to fruits, for every season. After each loop finishes every season has a reference to the same fruits array, which contain items calculated on the last step of the iterator. In your case, it's ["grape"].
Your problem is that you reuse the same fruits array for all the values. Even though you clear it, is is still the same array. If instead of fruits.clear you use fruits = [] then you won't have the issue.
You can see the issue in the following as an example:
arr = ['val']
hash = {
key1: arr,
key2: arr
}
p hash # => { key1: ['val'], key2: ['val'] }
arr.clear
p hash # => { key1: [], key2: [] }
You could alternatively use sorted[season] = fruits.clone or sorted[season] = [*fruits] ... anything that uses a new array.
You have to keep track of when you use 'mutation' methods (those that change objects in-place such as clear) - this is a common pitfall when working with hashes and arrays

dynamically finding value from key string in nested hash

I have a user inputed string called x_value whose value contains something like ticker|high. Whenever there is a |, that indicates that the latter is a child of the former. The purpose of the method is to return a specific value within a hash.
sections = []
object.x_value.split('|').each do |part|
sections << part.to_sym
end
I then want to drill down the data hash and retrieve the value of the last key.
data = {"ticker":{"high":529.5,"low":465,"avg":497.25,"vol":7520812.018}}
In this example
data[sections[0]][sections[1]] returns the expected 529.5 value. However, the user may have different hashes and different levels deep of nested key/values. How can I write this?
I have tried data[sections], but that didn't work.
Use Enumerable#reduce
data = {"ticker" => {"high" => 529.5, "low" => 465,"avg" => 497.25,"vol" => 7520812.018}}
"ticker|high".split('|').reduce(data) { |dat,val| dat[val] } #=> 592.5
more example:
data = {"more_ticker" => {"ticker" => {"high" => 529.5, "low" => 465,"avg" => 497.25,"vol" => 7520812.018}}}
"more_ticker|ticker|avg".split('|').reduce(data) { |dat,val| dat[val] }
#=> 497.25
You could also use recursion:
def getit(hash, x_value)
recurse(hash, x_value.split('|'))
end
def recurse(hash, keys)
k = keys.shift
keys.empty? ? hash[k] : recurse(hash[k], keys)
end
data = {"ticker" => {"high" => 529.5, "low" => 465}}
getit(data, "ticker|high") #=> 529.5
getit(data, "ticker") #=> {"high"=>529.5, "low"=>465}
data = {"more_ticker" => {"ticker" => {"high" => 529.5, "low" => 465}}}
getit(data, "more_ticker|ticker|low") #=> 465
getit(data, "more_ticker|ticker|avg") #=> nil

Clean way to search through hash keys in Ruby

I have a hash like so:
hash = {"jonathan" => "12", "bob" => 15 }
Then I have an array like so:
array = ['jon', 'bob']
I want to return the value of the key that includes a value in my array in the end.
The goal is something like this:
array.each do |name|
if hash.key.include? name
return hash.value
end
What's the best way to write that code block?
If you want multiple values, use Hash#values_at:
hash = {"jonathan" => "12", "bob" => 15 }
array = ['jon', 'bob']
hash.values_at(*array.select{|key| hash.has_key? key})
# => [15]
Using Array#&:
hash.values_at(*(hash.keys & array))
# => [15]
To get the keys, you don't need a block, this will do:
hash.keys & array
This takes the keys of the hash and intersect it with the array.
Second part, get a value from the hash:
hash[(hash.keys & array).last]
This will get the last key that is shared in hash and array and returns the value of that key in hash.
Also you can use select methods of Hash:
hash.select {| k,_ | array.include?( k ) }
# => {"bob"=>15}
and get, for example, last value:
hash.select {| k,_ | array.include?( k ) }.to_a.flatten.last
# => 15
or even use values_at method of Hash:
hash.values_at( *array ).compact.last
# => 15
Here is how I would write :
hash = {"jonathan" => "12", "bob" => 15 }
array = ['jon', 'bob']
array.collect(&hash.method(:[])).compact # => [15]

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

Finding hashes with items from the array using ruby

Sorry for missunderstanding for my fault I didn't check class of item...
I have an array:
array = [link1, link2, link3, link4, etc]
and array_of_hashes with two items: names and links
hash = [ :names, :links ] e.g.
array_of_hashes = [{ :names => name1, :links => link1}, {:names = name2, :links => link2}, ... ]
I want to do something with each pair of items from array_of_hashes which includes links from the array.
UPD: Revised data... sorry for missunderstanding.
It was a bit vague of a question but here is a shot at it.
you will need to reorder what you're trying to access from the hash, unless :name is required.
arr = ["link0", "link1",..."linkN"]
hsh = { "link0", item0, "link1", item1, "link1", item2,..."linkN", itemN}
hsh.each_pair | link, item |
do_something_foo(item) if arr.include?(link) # do_something_foo is a predefined function
end
jagga99 wrote
I want to do something with each item from hash which contain links from the array. Thanks a lot for your help.
If the [:name, :link] is the require pair, then you would need to identify which part is the item to do something with: the name or the link.
Enumerate the array of hashes and search the link array.
array = [:link1, :link2, :link3]
array_of_hashes = [{ :names => :name1, :links => :no_link}, {:names => :name2, :links => :link2}]
array_of_hashes.each do |hash|
if array.any? {|s| s==hash[:links]}
puts "Do something with #{hash[:names].to_s}"
end
end

Resources