Hash equivalent of large array search - ruby

I have a large array $actor_movie_array, which is sorted in this manner:
[actor1, movie1, movie2, movie3...]
[actor2, movie1, movie2, movie3...]
[actor3, movie1, movie2...]
The following method takes in a parameter actor_name, and then searches through $actor_movie_array for actor_name:
def search by actor(actor_name)
result_array = Array.new
$actor_movie_array.each {
|x| if x[0] == actor_name
result_array = x
result_array.delete_at(0)
break
end
}
return result_array
end
If found, it will be pushed into an empty array result_array that looks like this:
result_array = [actor1, movie1, movie2, movie3...]
Then, I will delete the first index of the array, which in this case is actor1, as I only want the remaining movies by this actor left in result_array.
However, this is really inefficient. I know that the hash equivalent would be more efficient, but do not know how to do it. Could anyone help to translate this into a hash equivalent?

OK, well, assuming you have a Hash of the form actors_to_movies = {actor1 => [movie1, movie2, movie3], actor2 => [movie4, movie5, movie6]}, you can just loop up lists of movies by actor the same way you'd look up anything in a Hash — for example actors_to_movies[actor1] would give [movie1, movie2, movie3].
If you're trying to figure out how to generate a hash from an array of the form you have, you'd do it like this:
actors_to_movies = Hash[ $actor_movie_array.map {|key, *vals| [key, vals] } ]
(Note that transforming a large array into a hashmap will take even longer than searching the array — but you'll ideally only have to do it once.)

I'd do as below
array_of_array = [ %w[actor1 movie1 movie2 movie3],
%w[actor2 movie1 movie2 movie3],
%w[actor3 movie1 movie2]
]
def search(ary,actor_name)
match = ary.find { |a| a.first == actor_name }
match.nil? ? "no actor found" : match[1..-1]
end
search(array_of_array,'actor2')
# => ["movie1", "movie2", "movie3"]
search(array_of_array,'actor5')
# => "no actor found"
Or, take the below approach :
array_of_array = [ %w[actor1 movie1 movie2 movie3],
%w[actor2 movie1 movie2 movie3],
%w[actor3 movie1 movie2]
]
hsh = Hash[ary.map { |key,*val| [key,val] }]
def search(hash,actor_name)
hash.fetch(actor_name,"no actor found")
end
search(hsh,'actor2')
# => ["movie1", "movie2"]
search(hsh,'actor5')
# => "no actor found"

Related

Looping an array and storing the values to a hash in ruby

I am trying to loop an array which might look like following:
names = ['sid','john'] #this array will be dynamic, The values keep changing
I am trying to write a method where I will define an empty hash and loop the array using .each
and then store the values to hash.But not working.
def add_address
names = ['sid','john']
addr_arr = {}
names.each do |n|
addr_arr['name'] = n
end
addr_arr
end
this returns only {"name"=>"john"}.
What am I doing wrong?
The problem with your implementation is that there's only one hash and each time you set a value for the "name" key, the previous value for that key will be deleted and replaced by the new value.
I see addr_arr has arr in the name, so I assume you wanted something like this:
def add_address
names = ['sid','john']
addr_arr = []
names.each do |n|
addr_arr << { "name" => n}
end
addr_arr
end
add_address
#=> [{"name"=>"sid"}, {"name"=>"john"}]
or shorter:
['sid','john'].map{ |name| {"name" => name} }
#=> [{"name"=>"sid"}, {"name"=>"john"}]
If you always use the key 'name', you're overwriting its values every time, I don't think that's what you want. I don't know if this is what you want anyway, but this should be enough to understand the problem
names.each do |n|
addr_arr[n] = n
end

Print particular values from object based on comparison

I am working on developing a UI wherein I need to retrieve particular values from an object based on conditions. object looks like below.
Category = {["personal_care_appliances", "glwise_category_config", "{catWhitelist: []}"],
["wine","glwise_category_config","{}"],
["shoes","glwise_category_config","{catWhitelist: []}"],
["automotive","glwise_category_config",],
["watch","glwise_category_config","{catWhitelist: []}"]
]
I need to print first element such as personal_care_appliances,shoes,watch as they have a component catWhitelist: [].
I tried using map and array syntax but it did not work.
PS. I am quite new to Ruby and learning through online docs.
Assuming that category is an array of arrays (which is not quite clear from your question), try map first element of nested array if the nested array includes "{catWhitelist: []}":
category.map { |e| e[0] if e.include? "{catWhitelist: []}" }.compact
# => ["personal_care_appliances", "shoes", "watch"]
Or, better to select required subarrays before to map it:
category.select { |e| e.include? "{catWhitelist: []}" }.map{ |e| e[0] }
If we assume you have this array:
category = [
["personal_care_appliances", "glwise_category_config", "{catWhitelist: []}"],
["wine","glwise_category_config","{}"],
["shoes","glwise_category_config","{catWhitelist: []}"],
["automotive","glwise_category_config",],
["watch","glwise_category_config","{catWhitelist: []}"]
]
and you want to get only the items which have catWhitelist, then you can use a method like:
def with_cat_whitelist(category)
result = []
category.each do |item|
result << item.first if item.last == "{catWhitelist: []}"
end
result
end

Ruby: Merge Hash containing Arrays

I am trying to merge two Hashes.I got the code from here. As you can see I want to add one more hobby to my list.
There are many hobbies. When h1 is formed ,only one hobby was available. When the second hobby arrived I wanted to merge it with the previous hash.
The structure of the Hashes are :
h1 = {"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling"}]}]}
h2 = {"students"=>[{"name"=>"bobby", "hobbies"=>[{"indoor"=>"reading"}]}]}
I ran this code:
res = h1.merge(h2) {|key,val1,val2| val1.merge val2}
The error I got was:
undefined method `merge' for [{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling"}]}]:Array (NoMethodError)
So since array is involved, I don't know what to do. Any Help.
Required Output:
{"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling", "indoor"=>"reading"}]}]}
The built-in marge method cannot do what you want.
You should write the original code to merge these structure like this.
def recursive_merge(h1, h2)
case h1
when Hash
(h1.keys + h2.keys).each do |key|
if h1.include?(key) && h2.include?(key) then
recursive_merge(h1[key], h2[key])
elsif h2.include?(key)
h1[key] = h2[key]
end
end
when Array
for i in 0...([h1.count, h2.count].max)
if i < h1.count && i < h2.count then
recursive_merge(h1[i], h2[i])
elsif i < h2.count then
h1[i] = h2[i]
end
end
end
end
recursive_merge(h1, h2)
puts h1
This method results the follwing output.
Please note that this method overwrites h1 itself.
{"students"=>[{"name"=>"bobby", "hobbies"=>[{"outdoor"=>"cycling", "indoor"=>"reading"}]}]}
The answer on your question might be:
h1.merge(h2) { |k, v1, v2|
v1.map.with_index { |v1e, idx|
v1e['hobbies'].first.merge! v2[idx]['hobbies'].first
v1e
}
v1
}
#⇒ {
# "students" => [
# [0] {
# "hobbies" => [
# [0] {
# "indoor" => "reading",
# "outdoor" => "cycling"
# }
# ],
# "name" => "bobby"
# }
# ]
# }
But I would definitely redesign hobbies to be a hash, not an array of hashes, eliminating weird first calls:
v1e['hobbies'].merge! v2[idx]['hobbies']
Hope it helps.

Ruby library function to transform Enumerable to Hash

Consider this extension to Enumerable:
module Enumerable
def hash_on
h = {}
each do |e|
h[yield(e)] = e
end
h
end
end
It is used like so:
people = [
{:name=>'fred', :age=>32},
{:name=>'barney', :age=>42},
]
people_hash = people.hash_on { |person| person[:name] }
p people_hash['fred'] # => {:age=>32, :name=>"fred"}
p people_hash['barney'] # => {:age=>42, :name=>"barney"}
Is there a built-in function which already does this, or close enough to it that this extension is not needed?
Enumerable.to_h converts a sequence of [key, value]s into a Hash so you can do:
people.map {|p| [p[:name], p]}.to_h
If you have multiple values mapped to the same key this keeps the last one.
[ {:name=>'fred', :age=>32},
{:name=>'barney', :age=>42},
].group_by { |person| person[:name] }
=> {"fred"=>[{:name=>"fred", :age=>32}],
"barney"=>[{:name=>"barney", :age=>42}]}
Keys are in form of arrays to have a possibility to have a several Freds or Barneys, but you can use .map to reconstruct if you really need.

difficulty modifying two dimensional ruby array

Excuse the newbie question. I'm trying to create a two dimensional array in ruby, and initialise all its values to 1. My code is creating the two dimensional array just fine, but fails to modify any of its values.
Can anyone explain what I'm doing wrong?
def mda(width,height)
#make a two dimensional array
a = Array.new(width)
a.map! { Array.new(height) }
#init all its values to 1
a.each do |row|
row.each do |column|
column = 1
end
end
return a
end
It the line row.each do |column| the variable column is the copy of the value in row. You can't edit its value in such way. You must do:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.each do |row|
row.map!{1}
end
return a
end
Or better:
def mda(width,height)
a = Array.new(width)
a.map! { Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
a = Array.new(width){ Array.new(height) }
a.map do |row|
row.map!{1}
end
end
Or better:
def mda(width,height)
Array.new(width) { Array.new(height){1} }
end
each passes into the block parameter the value of each element, not the element itself, so column = 1 doesn't actually modify the array.
You can do this in one step, though - see the API docs for details on the various forms of Array#new. Try a = Array.new(width) {|i| Array.new(height) {|j| 1 } }
you can create it like this?
a=Array.new(width) { Array.new(height,1) }
column in your nested each loop is a copy of the value at that place in the array, not a pointer/reference to it, so when you change its value you're only changing the value of the copy (which ceases to exist outside the block).
If you just want a two-dimensional array populated with 1s something as simple as this will work:
def mda(width,height)
[ [1] * width ] * height
end
Pretty simple.
By the way, if you want to know how to modify the elements of a two-dimensional array as you're iterating over it, here's one way (starting from line 6 in your code):
#init all its values to 1
a.length.times do |i|
a[i].length.times do |j|
a[i][j] = 1
end
end

Resources