Get element of an array by its contents - ruby

I'm sorry for weird wording. I don't know what you call this. I have this array:
[#<Item:0x007faeea066508 #name="Backpack", #exam="Not much here... just a backpack">]
This is an inventory array. The user will type something like "examine backpack" and it will return the #exam of of the element. How would you get that element only knowing #name? Thanks!

This is what you have:
class Item
# accessors for #name and #exam possibly defined here
def initialize(name, exam)
#name = name
#exam = exam
end
# methods possibly defined here
end
item = Item.new("Backpack", "Just a backpack")
#=> #<Item:0x0000010201dda0 #name="Backpack", #exam="Just a backpack">
So you have an array a with one element, an instance of class Item with the instance variables #name and #exam having the values "Backpack" and "Just a backpack", respectively:
a = [item]
#=> [#<Item:0x0000010201dda0 #name="Backpack", #exam="Just a backpack">]
You can use Object#instance_variable_get to retrieve the values of the instance variables #name and #exam, which I will store in local variables name and exam:
name = a.first.instance_variable_get(:#name) #=> "Backpack"
exam = a.first.instance_variable_get(:#exam) #=> "Just a backpack"
Now that you have the values of those variables, you can do whatever you like with them.

You probably want to select using a block (assuming your Item class responds to .name and .exam:
items = [#<Item:0x007faeea066508 #name="Backpack", #exam="Not much here... just a backpack">]
item = items.select {|i| i.name =~ /backpack/i }
puts item.exam

Here is my code:
articles = ArticleType.all
My result:
[#<ArticleType id: 1, name: "News", slug: "news">, #<ArticleType id: 2, name: "Articles", slug: "articles">]
To get the name's value of the first element:
articles[0].name
=> "News"
To get the name's values of your array:
ArticleType.all.map(&:name)
To get the elements that contains 'N':
ArticleType.all.map { |x| puts x.name if x.name.include?('N') }
=> News
I hope it helps you!

You can do this as following:--
user_typed_string = "examine backpack"
items = [#<Item:0x007faeea066508 #name="Backpack", #exam="Not much here... just a backpack">]
item = items.select {|i| user_typed_string.include?(i.name) }.last
exam = item.exam unless item.nil?

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

Can I improve this code using advance ruby concepts and I also wanted to print the list with item values?

Write a simple DSL for creating a shopping list. We should be able to specify the item name and quantity..
Something like.
My code works for pre-defined hash map, but once I take user input for creating a hash map it fails and also I want to improve my code by using more advanced concepts to achieve this result. Any suggestions ?
class Store
def initialize(item, quantity)
#products = { "item" => quantity }
#cart = []
end
def add_to_cart( item )
#cart << item
end
def add_product( item, price )
#products[item] = price
end
def cart_total
#cart.inject(0){|sum, item| sum + #products[item]}
end
def items
#products.join(', ')
end
end
puts "Please provide item name"
item = gets.chomp
puts "Please provide quantity associated with item"
quantity = gets.chomp.to_i
store = Store.new(item, quantity)
store.add_to_cart(item)
puts store.cart
printf "$%6.2f", store.cart_total
Expected Result:
s1.list #Should print complete list of the item and values added
sl.total # Should list the total price value for shopping done.
It's a quite broad question and it shouldn't, but I'd like to give my view on this anyway.
Initialize empty Hash with default values (Hash#new)
Don't forget attr_reader for accessing instance variable (Module#attr_reader)
Check the docs for the method used here (Hash, Array, Enumerable)
Here is a possible refactor:
class Store
attr_reader :cart, :products # you need to access the variables
def initialize
#cart = Hash.new(0) # initilize the Hash with default value
#products = Hash.new
end
def add_to_cart(item, quantity)
# increase by quantity, thanks to default value of the Hash,
# only if the product is available
if products.has_key? item # products thanks to the attr_reader, then see Hash docs
cart[item] += quantity
true
else
false
end
end
def add_product(item, price)
products[item] = price
end
def cart_total
cart.sum { |k, v| products[k] * v }
end
def products_list
products.keys
end
end
So, you can use this class as follows:
store = Store.new
store.add_product("Apple", 10.0 )
store.add_product("Lemon", 12.0 )
store.add_product("Melon", 20.0 )
store.products_list
#=> ["Apple", "Lemon", "Melon"]
store.products
#=> {"Apple"=>10.0, "Lemon"=>12.0, "Melon"=>20.0}
store.add_to_cart('Apple', 4) #=> true
store.add_to_cart('Melon', 5) #=> true
store.add_to_cart('Carrot', 1) #=> false # not available, not added
store.cart # thanks to attr_reader
#=> {"Apple"=>4, "Melon"=>2}
store.cart_total
#=> 140.0
You could think also to define a Cart class...

How do you check for matching keys in a ruby hash?

I'm learning coding, and one of the assignments is to return keys is return the names of people who like the same TV show.
I have managed to get it working and to pass TDD, but I'm wondering if I've taken the 'long way around' and that maybe there is a simpler solution?
Here is the setup and test:
class TestFriends < MiniTest::Test
def setup
#person1 = {
name: "Rick",
age: 12,
monies: 1,
friends: ["Jay","Keith","Dave", "Val"],
favourites: {
tv_show: "Friends",
things_to_eat: ["charcuterie"]
}
}
#person2 = {
name: "Jay",
age: 15,
monies: 2,
friends: ["Keith"],
favourites: {
tv_show: "Friends",
things_to_eat: ["soup","bread"]
}
}
#person3 = {
name: "Val",
age: 18,
monies: 20,
friends: ["Rick", "Jay"],
favourites: {
tv_show: "Pokemon",
things_to_eat: ["ratatouille", "stew"]
}
}
#people = [#person1, #person2, #person3]
end
def test_shared_tv_shows
expected = ["Rick", "Jay"]
actual = tv_show(#people)
assert_equal(expected, actual)
end
end
And here is the solution that I found:
def tv_show(people_list)
tv_friends = {}
for person in people_list
if tv_friends.key?(person[:favourites][:tv_show]) == false
tv_friends[person[:favourites][:tv_show]] = [person[:name]]
else
tv_friends[person[:favourites][:tv_show]] << person[:name]
end
end
for array in tv_friends.values()
if array.length() > 1
return array
end
end
end
It passes, but is there a better way of doing this?
I think you could replace those for loops with the Array#each. But in your case, as you're creating a hash with the values in people_list, then you could use the Enumerable#each_with_object assigning a new Hash as its object argument, this way you have your own person hash from the people_list and also a new "empty" hash to start filling as you need.
To check if your inner hash has a key with the value person[:favourites][:tv_show] you can check for its value just as a boolean one, the comparison with false can be skipped, the value will be evaluated as false or true by your if statement.
You can create the variables tv_show and name to reduce a little bit the code, and then over your tv_friends hash to select among its values the one that has a length greater than 1. As this will give you an array inside an array you can get from this the first element with first (or [0]).
def tv_show(people_list)
tv_friends = people_list.each_with_object(Hash.new({})) do |person, hash|
tv_show = person[:favourites][:tv_show]
name = person[:name]
hash.key?(tv_show) ? hash[tv_show] << name : hash[tv_show] = [name]
end
tv_friends.values.select { |value| value.length > 1 }.first
end
Also you can omit parentheses when the method call doesn't have arguments.

Convert List of Objects to Hash

In Ruby, I have a list of objects called Things with an Id property and a value property.
I want to make a Hash that contains Id as the key and Value as the value for the cooresponding key.
I tried:
result = Hash[things.map { |t| t.id, t.value }]
where things is a list of Thing
But this did not work.
class Thing
attr_reader :id, :value
def initialize(id, value)
#id = id
#value = value
end
end
cat = Thing.new("cat", 9)
#=> #<Thing:0x007fb86411ad90 #id="cat", #value=9>
dog = Thing.new("dog",1)
#=> #<Thing:0x007fb8650e49b0 #id="dog", #value=1>
instances =[cat, dog]
#=> [#<Thing:0x007fb86411ad90 #id="cat", #value=9>,
# #<Thing:0x007fb8650e49b0 #id="dog", #value=1>]
instances.map { |i| [i.id, i.value] }.to_h
#=> {"cat"=>9, "dog"=>1}
or, for Ruby versions prior to 2.0:
Hash[instances.map { |i| [i.id, i.value] }]
#=> {"cat"=>9, "dog"=>1}
result = things.map{|t| {t.id => t.value } }
The content of the outer pair of curly brackets is a block, the inner pair forms a hash.
However, if one hash is the desired result (as suggested by Cary Swoveland) this may work:
result = things.each_with_object({}){| t, h | h[t.id] = t.value}

Ruby - iterating through database results and storing them in a hash

Consider the following:
details = Hash.new
# Example of this Hash's final look
details["team1"] = "Example"
details["team2"] = "Another team"
details["foo"] = "Bar"
The way I get the names of the two teams is through:
teams = Match.find(1).teams
=> [#<Team id: 1, name: "England">, #<Team id: 2, name: "Australia">]
Now I would like to save the names of the teams into the Hash under team1 and team2. If I were using arrays I could do:
teams.each do |team|
details << team.name
end
However, I need to do this with the Hash I have shown above. How would one go about accomplishing this?
Hash[teams.map { |x| ["team_#{x.id}", x.name] }]
# => {"team_1"=>"England", "team_2"=>"Australia"}
If you want to keep id 1 and 2
Hash[a.map.with_index { |x,i| ["team_#{i.succ}", x.name] }]
# => {"team_1"=>"England", "team_2"=>"Australia"}
What about this?
teams.each_with_index do |team, idx|
id = "team#{idx + 1}"
details[id] = team.name
end
Here you take the team object and make hash key out of it, and then use that key to set a value.
How about using an inject for a one liner?
teams.inject({}){ |details, team| details["team#{team.id}"] = team.name; details }
The return value will be an Array or Hashes.
{}.tap do |h|
Match.find(1).teams.each_with_index {|t, i| h["team#{i+1}"] = t.name}
end

Resources