Classes in Ruby - Creating parameters that will accept multiple inputs - ruby

Ok guys, I am learning ruby and I am having a little bit of trouble with the tutorial. I was wondering if you could help me out!
Take the following code:
class Dish
def initialize(name, ingred, descrip)
#name = name
#ingred = ingred
#descrip = descrip
end
def name
#name
end
def name=(new_name)
#name = new_name
end
def ingred
#ingred
end
def ingred=(new_ingred)
#ingred = new_ingred
end
def descrip
#descrip
end
def descrip=(new_descrip)
#descrip = new_descrip
end
def display
puts "I am a #{#name} and my ingredient is #{#ingred} and my description is #{descrip}"
end
end
dis1 = Dish.new('Pizza', 'sauce', 'put sauce on that thing')
dis1.display
Ok so here is my question and I hope I explain it well enough. So far I have learned to take enter one parameter when making a new instance of a class (i.e. (name, ingred, descrip)). What I am wondering is if a dish has multiple ingredients, how would I add that to my class? Also, if I wanted to count the number of ingredients or the number of names, how would I do that. I am just learning about classes and I am having trouble matching the exact wording I would Google for. Thanks!

I'll try to answer some of your questions. To simplify, I removed your variable, descrip and its associated methods. You see I've put a * in front of ingred in initialize. This means that a variable number of arguments are passed after name. This is one way of dealing with your question about having multiple ingredients. Here ingred is an array. Since #ingred is set equal to ingred, #ingred is also an array. If you look at the various methods and what some print when invoked (shown at the bottom), you should be able to see how this works. (Edited to add a bit of functionality. You may need to scroll down at the bottom.)
class Dish
def initialize(name, *ingred)
#name = name
#ingred = ingred
end
def name
#name
end
def name=(new_name)
#name = new_name
end
def ingred
#ingred
end
def ingred=(*ingred)
#ingred = ingred
end
def add_ingred(ingred)
#ingred << ingred
end
def remove_ingred(ingred)
#ingred.delete(ingred)
end
def nbr_ingred
#ingred.count
end
end
dis1 = Dish.new("Pizza", "sauce", "crust", "cheese", "anchovies")
p dis1.ingred #=> ["sauce", "crust", "cheese", "anchovies"]
dis1.add_ingred("olives")
p dis1.ingred #=> ["sauce", "crust", "cheese", "anchovies", "olives"]
dis1.add_ingred(["capers", "mushrooms"])
p dis1.ingred #=> ["sauce", "crust", "cheese", "anchovies", "olives", ["capers", "mushrooms"]]
dis1.ingred.flatten!
p dis1.ingred #=> ["sauce", "crust", "cheese", "anchovies", "olives", "capers", "mushrooms"]
dis1.remove_ingred("anchovies")
p dis1.ingred #=> ["sauce", "crust", "cheese", "olives", "capers", "mushrooms"]
p dis1.nbr_ingred #=> 6
dis1.ingred = "olives", "pineapple" # treated as ["olives", "pineapple"]
p dis1.ingred #=> [["olives", "pineapple"]]
dis1.ingred = ["cheese", "crust"]
p dis1.ingred #=> [["olives", "pineapple"]]
dis1.ingred.flatten!
p dis1.ingred #=> ["olives", "pineapple"]

Use arrays.
class Dish
class Ingredient
attr_accessor :name, :description
def initialize(name, description)
#name = name
#description = description
end
def to_s
"#{name} - #{description}"
end
end
attr_accessor :name, :description, :ingredients
def initialize(name, description)
#name = name
#description = description
#ingredients = []
end
def to_s
"#{name} - #{description}\n #{ingredients.join("\n").to_s}"
end
end
pizza = Dish.new("Pizza", "Italian style pizza")
pizza.ingredients << Dish::Ingredient.new("Tomato Sauce", "Juicy Juicy Tomato Sauce.")
pizza.ingredients << Dish::Ingredient.new("Cheese", "Cheese, duh.")
puts pizza.to_s

As the two answers before me did both leave out the description parameter i will stealCary Swoveland's answer and add the descrip parameter:
class Dish
attr_accessor :name, :descrip
def initialize(name, *ingred, descrip) # Only in Ruby 1.9+
#name = name
#ingred = *ingred
#descrip = descrip
end
def ingred
#ingred
end
def ingred=(*ingred)
#ingred = ingred
end
def add_ingred(ingred)
#ingred << ingred
end
def remove_ingred(ingred)
#ingred.delete(ingred)
end
def nbr_ingred
#ingred.count
end
def display
puts "I am a #{#name} and my ingredient is #{#ingred.join(', ')} and my description is #{descrip}"
end
end
dis1 = Dish.new('Pizza', 'sauce', 'ham', 'put ingredients on that thing.')
dis1.add_ingred('fish')
dis1.display #=> I am a Pizza and my ingredient is sauce, ham, fish and my description is put ingredients on that thing.

Related

Failed to pass the value of an instance variable to another from within the same class (Ruby)

this is my code.
class Dog
attr_accessor :name
attr_reader :breed, :age
def initialize(name, breed, age)
#name = name
#breed = breed
#age = age
#distance_in_kms = []
end
def walk(distance_in_kms)
#distance_in_kms << {distance: distance_in_kms}
end
def walked_distance
#walked_distance = #distance_in_kms.inject(0) {|sum, hash| sum + hash[:distance]}
end
def display_walks
puts "#{#name} has had #{#distance_in_kms.length} walks and walked #{#walked_distance} kms today:"
#distance_in_kms.each do |each_walk|
puts "#{each_walk[:distance]} km"
end
end
#overriding to_s to print a meaningful representation of a class
def to_s
return "Dog: breed-#{#breed} name-#{#name}"
end
end
doggo = Dog.new("Roy", "Labrador", 8)
doggo.walk(3)
doggo.walk(5)
doggo.walk(1)
puts doggo.name = "Dang"
puts doggo.breed
doggo.display_walks
The result I have is this
Dang
Labrador
Dang has had 3 walks and walked kms today:
3 km
5 km
1 km
The expected value before kms should be 9, the sum of each walk in a day. Obviously, I have done something wrong with the walked_distance method. I was trying to pass the value of array #distance_in_kms into #walked_distance, but it didn't work, the return value was nil.But what can I do to fix the problem and get expected results? Thank you!!
You must call the method to update the #walked_distance, for example:
def display_walks
walked_distance # <------ This line
puts "#{#name} has had #{#distance_in_kms.length} walks and walked #{#walked_distance} kms today:"
#distance_in_kms.each do |each_walk|
puts "#{each_walk[:distance]} km"
end
end
Or change the walked_distance method and call it directly from the string interpolation:
def walked_distance
#distance_in_kms.inject(0) {|sum, hash| sum + hash[:distance]}
end
def display_walks
puts "#{#name} has had #{#distance_in_kms.length} walks and walked #{walked_distance} kms today:"
#distance_in_kms.each do |each_walk|
puts "#{each_walk[:distance]} km"
end
end
Off topic, just a small change to evaluate:
#walked_distance = #distance_in_kms.values.sum
puts "#{#name} has had #{#distance_in_kms.length} walks and walked #{#walked_distance} kms today:"
But you never call the code to calculate #walked_distance. Either update it when you add a new walk or calculate total distance here (by calling walked_distance, for example)
I could've done this right at the beginning too:
class Dog
attr_accessor :name
attr_reader :breed, :age
def initialize(name, breed, age)
#name = name
#breed = breed
#age = age
#distance_in_kms = []
#walked_distance = []
end
def walk(distance_in_kms)
#distance_in_kms << {distance: distance_in_kms}
#walked_distance = #distance_in_kms.inject(0) {|sum, hash| sum + hash[:distance]}
end
def display_walks
puts "#{#name} has had #{#distance_in_kms.length} walks and walked #{#walked_distance} kms today:"
#distance_in_kms.each do |each_walk|
puts "#{each_walk[:distance]} km"
end
end
#overriding to_s to print a meaningful representation of a class
def to_s
return "Dog: breed-#{#breed} name-#{#name}"
end
end
doggo = Dog.new("Roy", "Labrador", 8)
doggo.walk(3)
doggo.walk(5)
doggo.walk(1)
puts doggo.name = "Dang"
puts doggo.breed
doggo.display_walks
or use class methods to create a counter then try to link it to the instances(It's a very long way though)
Indeed, understanding variable scope is a real struggle for a beginner. Anyway, much appreciation for anyone who answered my question. Thank you!

How do I pull a name from a class in an Array?

Okay, this is a little hard to explain but I will try (For starters I am only just learning to code so it may be something super simple I'm missing..)
I created a few classes, I put a name in those classes, I put them in an array, I then chose one at random and try to puts the name, and it outputs blank.
Am I doing this all completely wrong? I've been learning ruby for about 3 months now so I'm sure there is a lot I don't know.
class A
attr :name
def set_name
#name = "Aaa"
end
def get_name
return #name
end
end
class B
attr :name
def set_name
#name = "Bbb"
end
def get_name
return #name
end
end
class C
attr :name
def set_name
#name = "Ccc"
end
def get_name
return #name
end
end
name_a = A.new
name_b = B.new
name_c = C.new
which_name = Array.new
which_name[0] = name_a
which_name[1] = name_b
which_name[2] = name_c
roll = rand(max 3)
puts which_name[roll].get_name
I then chose one at random and try to puts the name, and it outputs
blank.
You never called the #set_name method in your code. You can add this:
name_a.set_name
name_b.set_name
name_c.set_name
Also, you probably want to look into #attr_accessor.

How do I randomly select a name from an array and check if it's uppercase

I need to randomly pick a name from an array in Ruby and then check if it uppercase. So far I have:
def namegenerator
return #name.sample
end
def namechecker
if name.upcase then
check = TRUE
else
check = FALSE
end
end
It needs to be as two separate methods like this.
Something like this:
def sample_word(words)
words.sample
end
def upcase?(word)
word == word.upcase
end
And then something like:
words = %w[APPLE banana CherRy GRAPE]
word = sample_word(words)
puts word # e.g. BANANA
puts upcase?(word) # will print true
If you just want to check just the first letter:
names = %w(Kirk mccoy scott Spock)
names.sample.then { |name| [name, name[0] == name[0].upcase] }
#=> ["mccoy", false]
Maybe something like this:
class NameGenerator
def initialize(size, items)
#name = ""
#size = size
#items = items
end
def namegenerator
#name = #items.sample(#size).to_s
end
def namechecker?
#name == #name.upcase
end
def name
#name
end
end
ng = NameGenerator.new 1, ["name", "Name", "NAME"]
ng.namegenerator
puts ng.name, ng.namechecker?
Update
I've posted code without much thinking about abstraction and i think it would be much better to encapsulate name and upper case check to separate class and make it immutable, then make generator class that selects one entity from collection.
class NameGenerator
def initialize(items)
#items = items
end
def next
#items.sample
end
end
class Name
attr_reader :name
def initialize(name)
#name = name
end
def is_uppercase?
#name.match(/\p{Lower}/) == nil
end
end
ng = NameGenerator.new [
Name.new("name"),
Name.new("Name"),
Name.new("NAME"),
Name.new("na-me")
]
name = ng.next
puts name.name, name.is_uppercase?

A ruby code to get input from user

kindly give an example, i am new to return number of ingredients. new to ruby and having prob with syntax.
class Recipe
include Enumerable
attr_reader :name
def initialize(name, cuisine, ingredients, steps)
#name = name
#cuisine = cuisine
#ingredients = ingredients
#steps = steps
end
def name
#name
end
def cuisine
#cuisine
end
def ingredients
#ingredients
end
def steps
#steps
end
def display
puts "Recepie Manager"
puts "\n Name:", #name, "\n Cuisine:", #cuisine ,"\n Ingredients and Quantity:", #ingredients, "Steps:", #steps
end
def how_many_ingredients
puts "Number of Ingridents:" , #ingredients
end
end
obj1 = Recipe.new('Briyani', 'Indian','chicken,rice,spices,blah,blah','cook and eat')
obj1.display
From the code it looks like you have misplaced your end statement. You have to create objects of class after it ends.
class Receipe
include Enumerable
attr_reader :name
def initialize(name, cuisine, ingredients, steps)
#name = name
#cuisine = cuisine
#ingredients = ingredients
#steps = steps
end
def name
#name
end
def cuisine
#cuisine
end
def ingredients
#ingredients
end
def steps
#steps
end
def display
puts "Recepie Manager"
puts "\n Name:", #name, "\n Cuisine:", #cuisine ,"\n Ingredients and Quantity:", #ingredients, "Steps:", #steps
end
def how_many_ingredients
puts "Number of Ingridents:" , #ingredients
end
end
obj1 = Receipe.new('Briyani', 'Indian','chicken,rice,spices,blah,blah','cook and eat')
obj1.display
Thanks,
I used my guru "Google" for finding solution. For returning the ingredients count i used split and count methods.
def how_many_ingredients
ingredients = #ingredients.split(",")
puts "Number of Ingridents:" , ingredients.count
end
you can use gets.chomp to get input from users if thats what you mean
class Receipe
include Enumerable
attr_reader :name
def initialize(name, cuisine, ingredients, steps)
#name = name
#cuisine = cuisine
#ingredients = ingredients
#steps = steps
end
def name
#name
end
def cuisine
#cuisine
end
def ingredients
#ingredients
end
def steps
#steps
end
def display
puts "Recepie Manager"
puts "\n Name:", #name, "\n Cuisine:", #cuisine ,"\n Ingredients and Quantity:", #ingredients, "Steps:", #steps
end
def how_many_ingredients
puts "Number of Ingridents:" , #ingredients
end
end
yourname = gets.chomp
yourcuisine = gets.chomp
youringridents = gets.chomp
yoursteps = gets.chomp
obj1 = Receipe.new(yourname, yourcuisine,youringridents,yoursteps)
obj1.display

How can I combine two blocks to simplify my code?

Hi I am a student learning Ruby. I am using the quick start guide at ruby-lang.org, which has some examples of Ruby basics.
I studied the MegaGreeter class, and I am trying to figure out how to puts two arguments (name and age) in the same each block in order to simplify my code.
I think there would be another way. (Using regular loops instead of each.)
Calculate the array's size.
Use a loop like in C.
But I want to use the each loop. Below is my code:
class MegaGreeter
attr_accessor :name
attr_accessor :age
#Creat the object
def initialize(name=nil, age=0)
#name = name
#age = age
#tmp = Array.new()
#i = 0
end
#Say hi to everybody
def say_hi
if #name.nil?
puts "please give me the input !!"
elsif #name.respond_to?("each")
#list responding
#name.each do |name|
#tmp[#i] = "hi ~! #{name}"
#i += 1
end
#i=0
#age.each do |age|
#tmp[#i] += " and you are #{age} years old"
puts #tmp[#i]
#i += 1
end
else
puts "give me array ~"
end
end
end
a = MegaGreeter.new()
a.name = ["juno","yoonhe"]
a.age = [1,2]
a.say_hi
You can use the Array method zip to first combine your two arrays. It groups the elements by their position in the array, so the first element of the #name array will be grouped with the first element of the #age array and so on.
#name = ['Foo', 'Bar']
#age = [23, 41]
name_and_age = #name.zip(#age)
# [['Foo', 23], ['Bar' 41]]
Now the names and ages are grouped together, and you can iterate over them using each.
name_and_age.each do |name, age|
puts name, age
end
# Foo 23
# Bar 41
Putting it back into your original code:
class MegaGreeter
attr_accessor :name, :age
#Creat the object
def initialize(name = nil, age = 0)
#name = name
#age = age
end
#Say hi to everybody
def say_hi
if #name.nil?
puts "please give me the input !!"
elsif #name.respond_to?("each")
#list responding
#name.zip(#age).each do |name, age|
puts "hi ~! #{name} and you are #{age} years old"
end
else
puts "give me array ~"
end
end
end

Resources