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
Related
I have written a Travel App with Tour Items and I'm trying to display the order at the end.
When I use puts #order_items I get {"SevendaySurfSportTour"=>2} for two tours.
I would like it to display
2 SevendaySurfSportTour at the end. But I don't know how, any help would be good?
class TourOrder
def initialize
#order_items = Hash.new(0)
end
def add_item(name, quantity)
#order_items[name] += quantity
end
def get_items
return #order_items
end
def display
puts "Thank you for coming!"
puts #order_items
end
end
#order_items is a hash and what has printed out if the string representation of such a hash. When you want to format the output differently, then you have to implement that on your own – for example like this:
def display
puts "Thank you for coming!"
#order_items.each do |name, quantity|
puts "#{quantity} #{name}"
end
end
Below code works for you
class TourOrder
def initialize
#order_items = Hash.new(0)
end
def add_item(name, quantity)
#order_items[name] += quantity
end
def get_items
#order_items
end
def display
puts "Thank you for coming!"
puts items
end
def items
#order_items.collect{ |k, v| "#{v} #{k}"}
end
end
t = TourOrder.new
t.add_item('SevendaySurfSportTour', 2)
t.add_item('Foo', 4)
t.add_item('Bar', 1)
t.display
=> Thank you for coming!
2 SevendaySurfSportTour
4 Foo
1 Bar
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!
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?
I'm writing a short program that asks a user to enter a car model, maker, and year input and it passes that input through an algorithm. My question is, is there a way to label multiple printed outputs after it has been put through the formula to where it will number each output? Would I have to use a for each loop? I'm just trying to get a general idea of how I would accomplish this.
say for example the printed output would look like this.
class Car
attr_reader :make, :model, :year
def initialize
end
def set_make(make)
#make = make
end
def set_model(model)
#model = model
end
def set_year(year)
#year = year
end
def get_make
#make
end
def get_year
#year
end
def get_model
#model
end
end
array_of_cars = Array.new
print "How many cars do you want to create? "
num_cars = gets.to_i
for i in 1..num_cars
puts
print "Enter make for car #{i}: "
make = gets.chomp
print "Enter model for car #{i}: "
model = gets.chomp
print "Enter year of car #{i}: "
year = gets.to_i
c = Car.new
c.set_make(make)
c.set_model(model)
c.set_year(year)
array_of_cars << c
end
puts
puts "You have the following cars: "
puts
for car in array_of_cars
puts "#{car.get_year} #{car.get_make} #{car.get_model}"
end
puts
2014 Ford Expedition
2017 Toyota 86
2017 Aston Martin DB11
is there a way to add those numbers to the output?
Instead using a for loop you could try using each_with_index, which will allow you to get each element inside the array_of_cars and also the index for each element, in this case adding 1 to the current index will give you the value starting from 1:
array_of_cars.each_with_index do |car, index|
puts "#{index + 1}. #{car.get_year} #{car.get_make} #{car.get_model}"
end
Or you can use each and with_index passing the first element, in this case 1 as argument:
array_of_cars.each.with_index(1) do |car, index|
puts "#{index}. #{car.get_year} #{car.get_make} #{car.get_model}"
end
You don't need so many methods. Use attr_accessor to set getters and setters and utilize initialize better. Then using the basic idea from this answer by tadman, we can collect newly made objects into an array within the class itself. All together we can compress your class to:
class Car
attr_accessor :make, :model, :year
def self.all
#all ||= []
end
def initialize(make, model, year)
#make = make
#model = model
#year = year
Car.all << self
end
end
We can use times to run a piece of code n times.
puts "How many cars do you want to create? "
n = gets.to_i
n.times.with_index(1) { |_,i|
puts "Enter make for car #{i}"
make = gets.chomp
puts "Enter model for car #{i}: "
model = gets.chomp
puts "Enter year of car #{i}: "
year = gets.to_i
puts
Car.new(make, model, year)
}
Then as Sebastián Palma has already suggested, use each.with_index(1) to print your cars. The argument offsets the index by 1.
Car.all.each.with_index(1) { |c, i| puts "#{i}. #{c.year} #{c.make} #{c.make}" }
Sidenotes: 1. Avoid using for loops in Ruby 2. Use puts not print.
I have this class:
class Player
attr_accessor :card_pile, :name
def initialize
#name = name
#bust = false
#card_pile = []
end
def bust?
return #cards.inject(:+) > 21
end
end
I also have this as the beginning of another class
def playing_game
puts "How many players are playing? "
players_amount = gets.chomp.to_i
(0...players_amount).each do
puts ("What is the players name? ")
#name = gets.chomp
#players.push(#name)
end
#players.each do |each_player|
#name = Player.new
while true
while #name.card_pile.length < 2 do
new_card = Card.new
#name.card_pile.push(new_card.value)
end
puts(#name.card_pile)
print #name, "'s turn" "\n"
At the moment this will print out #<Player:0x007fc14984a4b0>'s turn instead of Rich's turn
Why is this happening? I thought I had made an instance variable in the Player class and then instantiated this class #name = Player.new and then could reference it from here on out??
This will help
def playing_game
puts 'How many players are playing?'
players_amount = gets.chomp.to_i
players_names = (0...players_amount).map do
puts ("What is the players name? ")
gets.chomp
end
players_names.each do |player_name|
player = Player.new(player_name)
while player.card_pile.length < 2 do
new_card = Card.new
player.card_pile.push(new_card.value)
end
puts player.card_pile
puts "#{player.name}'s turn"
end
end
UPD:
You don't need instance variables (like #name and #players inside single method).
In this code you iterate over players names
#players.each do |each_player|
=>
players_names.each do |player_name|
In context of
#name = Player.new
name is a Player instance
to create player with given name pass it to initializer:
player = Player.new(player_name)
then call name on Player instance, that you create earlier
puts "#{player.name}'s turn"