For my program, I have a list of things that's sort of like a spreadsheet that I'm trying to print to the console. I want it to look something like
garage 1
car: jaguar
price: $134,000
car: mercedes
price: $234,000
garage 2
car: jaguar
price: $134,000
garage is a hash with each key being garage number and cars in a specific garage being pushed to each key, eg garage[0] = [car1, car2]
Class car holds the information printed, and has these definitions
class Car
attr_accessor :car_name, :car_price
def initialization(name, price)
#car_name = name
#car_price = price
end
def name
#car_name
end
def price
#car_price
end
end
I've tried to implement such
garage.each do |x|
print "garage "
print garage.index(x) + 1
puts " "
x.each do |y|
printf("%10s", "Car: ")
puts y.name.rjust(16)
printf("%10s", "Price: $")
puts y.price.to_s.rjust(16)
puts " "
end
end
But I'm not getting the desired output. Depending on the car name, the spaces in between will be too far and the output won't be aligned.
I'm wondering if there's a method where I can format an entire line output? Basically like chaining together strings and other variables such as integers and floats and setting their distances correctly so that I can customize each line that's output to console.
First create the Car class:
class Car
attr_accessor :car_name, :car_price
def initialize(name, price)
#car_name = name
#car_price = price
end
end
Note that the method is initialize, not initialization, and that the class method attr_accessor creates both getter and setter methods for each of the two instance variables, so there is no need for the explicit getter methods.
Now let's create a couple of instances of this class.
jag = Car.new('jaguar', 134000)
#=> #<Car:0x00007f839690d2d0 #car_name="jaguar", #car_price=134000>
mb = Car.new('mercedes', 234000)
#=> #<Car:0x00007f83990b1d90 #car_name="mercedes", #car_price=234000>
Suppose the hash garage is as follows.
garage = { 1=>[jag, mb], 2=>[jag] }
#=> {1=>[#<Car:0x00007f839690d2d0 #car_name="jaguar", #car_price=134000>,
#=> #<Car:0x00007f83990b1d90 #car_name="mercedes", #car_price=234000>],
# 2=>[#<Car:0x00007f839690d2d0 #car_name="jaguar", #car_price=134000>]}
Firstly, we will need a way to display car prices in the correct format, starting with a dollar sign and with thousands separated by commas. One way to do that is as follows.
def convert_price_to_str(car_price)
s = ''
loop do
car_price, three = car_price.divmod(1000)
break "$#{three}#{s}" if car_price.zero?
s = (",%03d" % three) + s
end
end
For example,
convert_price_to_str 34 #=> "$34"
convert_price_to_str 3042 #=> "$3,042"
convert_price_to_str 49621 #=> "$49,621"
convert_price_to_str 1324534 #=> "$1,324,534"
See Integer#divmod, a very handy (and underutilized) method.
To display the values in the desired format it is necessary to determine the length of the longest car name or formatted price.
longest = garage.values.flatten.map do |car|
[car.car_name.size, convert_price_to_str(car.car_price).size].max
end.max
#=> 8
Now let's write another helper method to display car names and prices.
def print_name_and_price(name, price, longest)
puts " car: #{name.rjust(longest)}"
puts " price: #{convert_price_to_str(price).rjust(longest)}"
puts
end
For example,
print_name_and_price('jaguar', 134000, longest)
displays the three lines (the last being empty):
car: jaguar
price: $134,000
See String#rjust.
We may now put all this together.
garage.each do |g,cars|
puts "garage #{g}"
cars.each do |car|
print_name_and_price(car.car_name, car.car_price, longest)
end
end
displays:
garage 1
car: jaguar
price: $134,000
car: mercedes
price: $234,000
garage 2
car: jaguar
price: $134,000
Related
I have:
ary = [
'ahorseride', 'amparkeetjump', 'acatlive', 'adogwish', 'bmparkeetjump',
'bcatlive', 'bdogwish', 'bhorseride', 'brabuffallo', 'chorseride7679',
'ceelionking5454', 'crabuffallokjkj4', 'dgiraffeoiu9-0', 'chorseride767',
'ccatlive', 'dcatlive', 'ddogwish', 'emparkeetjump', 'emouse', 'eeelionking',
'erabuffallo', 'ffhorseride7679', 'fgeelionking5454', 'fhcrabuffallokjkj4a',
'fkcgiraffeoiu9087*s',
]
big_animal = ['horse', 'lion', 'buffallo', 'giraffe']
For each element in big_animal, I would like to find which elements of ary include that as a substring, and present them in a specific order. I would like to achieve this result:
horse in the chorseride7679
lion in the ceelionking5454
buffallo in the crabuffallokjkj4
giraffe in the dgiraffeoiu9-0
and/or
horse in the ffhorseride7679
lion in the fgeelionking5454
buffallo in the fhcrabuffallokjkj4a
giraffe in the fkcgiraffeoiu9087*s
How do I do this? My attempt is:
horse = big_animal[0]
ary.each do |e|
puts "#{horse} in the house of #{e}" if e.include?(horse)
end
whose result is:
horse in the house of ahorseride
horse in the house of bhorseride
horse in the house of chorseride7679
horse in the house of chorseride767
horse in the house of ffhorseride7679
It appears that you wish to find four consecutive elements of ary that respectively include the strings given as elements of big_animal. If that is true all such consecutive elements from ary could be obtained as follows.
ary.each_cons(big_animal.size).select do |words|
big_animal.each_index.all? { |i| words[i].include?(big_animal[i]) }
end
#=> [["chorseride7679", "ceelionking5454", "crabuffallokjkj4", "dgiraffeoiu9-0"],
# ["ffhorseride7679", "fgeelionking5454", "fhcrabuffallokjkj4a",
# "fkcgiraffeoiu9087*s"]]
See Enumerable#each_cons.
First of all, I have to admit that this is the very first time I'm using Ruby which is why my code might be a bit messy.
On a second note, I've seen the answer from nPn getting the output you want to have with a regex. However, this is not completely solving your problem because his solution will only retrieve values containing the big_animal and ending with a digit. This solution does not care about the right order of the houses.
As far as I've understood your problem, you want all the houses that contain the big_animal but only if they are in the same order. This is why I've come up with this piece of code:
ary = ['ahorseride', 'amparkeetjump', 'acatlive', 'adogwish', 'bmparkeetjump', 'bcatlive', 'bdogwish', 'bhorseride', 'brabuffallo', 'chorseride7679', 'ceelionking5454', 'crabuffallokjkj4', 'dgiraffeoiu9-0', 'chorseride767', 'ccatlive', 'dcatlive', 'ddogwish', 'emparkeetjump', 'emouse', 'eeelionking', 'erabuffallo', 'ffhorseride7679', 'fgeelionking5454', 'fhcrabuffallokjkj4', 'fkcgiraffeoiu9087', ]
big_animal = ['horse', 'lion', 'buffallo', 'giraffe']
count = 0
houses = Array.new(big_animal.length)
while count < ary.length do
animals = 0
if ary[count].include?(big_animal[animals])
while animals < big_animal.length do
if ary[count+animals].include?(big_animal[animals])
houses[animals] = ary[count+animals]
if animals == big_animal.length-1
puts houses
end
else
houses = Array.new(big_animal.length)
end
animals = animals + 1
end
end
count = count + 1
end
The above code gives me the following output:
chorseride7679
ceelionking5454
crabuffallokjkj4
dgiraffeoiu9-0
ffhorseride7679
fgeelionking5454
fhcrabuffallokjkj4
fkcgiraffeoiu9087
You can also try it online here and change the input array on that website to test different scenarios. I'd be happy to know if this is what you were searching for.
Just guessing that you are looking for the first animal with the matching name , ending in a digit.
ary = ['ahorseride', 'amparkeetjump', 'acatlive', 'adogwish', 'bmparkeetjump',
'bcatlive', 'bdogwish', 'bhorseride', 'brabuffallo', 'chorseride7679',
'ceelionking5454', 'crabuffallokjkj4', 'dgiraffeoiu9-0', 'chorseride767',
'ccatlive', 'dcatlive', 'ddogwish', 'emparkeetjump', 'emouse', 'eeelionking',
'erabuffallo', 'ffhorseride7679', 'fgeelionking5454', 'fhcrabuffallokjkj4', 'fkcgiraffeoiu9087' ]
big_animal = ['horse', 'lion', 'buffallo', 'giraffe']
big_animal.each do |ba|
rs = "#{ba}.*\\d$"
rexp = Regexp.new rs
entry = ary.find { |i| i.match(rs) }
puts "#{ba} in the house of #{entry}"
end
#horse in the house of chorseride7679
#lion in the house of ceelionking5454
#buffallo in the house of crabuffallokjkj4
#giraffe in the house of dgiraffeoiu9-0
https://repl.it/NxGr
By brazenly overriding String method. Better do not use this approach -))
class String
def in_house? other
(other.is_a? String) && (other.include? self)
end
def in_house_message_for other
puts "#{self} in house of #{other}" if self.in_house? other
return
end
end
then
ary.each do |house|
big_animal.each do |animal|
animal.in_house_message_for(house)
end
end
or that way to print in same order with big_animal array
big_animal.each do |animal|
ary.each do |house|
animal.in_house_message_for(house)
end
end
the second one will print
horse in house of ahorseride
horse in house of bhorseride
horse in house of chorseride7679
horse in house of chorseride767
horse in house of ffhorseride7679
lion in house of ceelionking5454
lion in house of eeelionking
lion in house of fgeelionking5454
buffallo in house of brabuffallo
buffallo in house of crabuffallokjkj4
buffallo in house of erabuffallo
buffallo in house of fhcrabuffallokjkj4
giraffe in house of dgiraffeoiu9-0
giraffe in house of fkcgiraffeoiu9087
I have a few syntax errors on my program that are really bothering me. I can't seem to figure out how to fix them as I am new to ruby. The first error is on the title, I have a few more I'm sure. The purpose of the program is to create cars with make, model, and year and have user input how many cars they want then display all of them at the end.
Can someone point me to the right direction?
Here is my code:
class Car
def initialize(make, model, year)
#make = make
#model = model
#year = year
end
print "How many cars do you want to create? "
array_of_cars = Array.new
num_cars = gets.to_i
c = car.new
for i in 1..num_cars
end
puts
print "Enter make for car #{i}: "
make = gets.chomp
print "Enter model for car #{i}: "
model = gets.chomp
print "Enter year for car #{i}: "
year = gets.to_i
c.set_make(make)
c.set_model(model)
c.set_year(year)
array_of_cars << c
end
puts
puts "You have the following cars: "
for car in array_of_cars
puts "#{car.get_year} #{car.get_make} #{car.get_model}"
end
In ruby, class names are constants, so should start with a capital letter, as in class Car. When creating a new object of that class, you call new on the class itself. So you would change car.new into Car.new.
You will also need to define your set_* and get_* methods inside the class, but since this is a common pattern, ruby has attr_accessor available. See this answer for a full explanation of attr_accessor.
Consider that your Car does not do anything, it contains only data and has no methods. When this happens, consider making it a Struct instead of a class. A Struct generates a reader and writer method automatically without even specifying attr_reader.
Car = Struct.new(:make, :model, :year)
array_of_cars = Array.new
while true
puts
print "Enter make for car ('x' to exit): "
make = gets.chomp
break if make == 'x'
print "Enter model for car: "
model = gets.chomp
print "Enter year for car: "
year = gets.to_i
array_of_cars << Car.new(make, model, year)
end
puts
puts 'You have the following cars:' # sorted by year for fun
array_of_cars.sort_by{ | car | car.year }.each do | car |
puts "#{car.year} #{car.make} #{car.model}"
end
A few pieces of advice.
Run Ruby with the -w option :
$ ruby -w cars.rb
cars.rb:17: warning: mismatched indentations at 'end' with 'for' at 16
cars.rb:34: warning: mismatched indentations at 'end' with 'class' at 1
cars.rb:41: warning: mismatched indentations at 'end' with 'for' at 39
and eliminate the cause of warnings.
$ ruby -w cars.rb
How many cars do you want to create? 2
cars.rb:2:in `initialize': wrong number of arguments (given 0, expected 3) (ArgumentError)
from cars.rb:13:in `new'
from cars.rb:13:in `<main>'
new calls initialize, so new must have the same number of arguments
as parameters in initialize. Hence a car can be created only after you have asked all the information.
Don't work in the class. As written, your code is executed when Ruby reads
the class definition. For this exercise, you can leave it in the main level outside the class definition, or put it into a method.
for i in 1..num_cars
end
This loop is empty and does nothing. And prefer powerful iterators instead of this C, Perl, Java style (for, while, etc).
I define strings with apostrophes and keep double quotes when interpolation is needed (even if it's a question of nano seconds and personal choice). See here and there.
If you want to be comfortable with Ruby programming, I recommend The Pickaxe.
There are many ways of doing things in Ruby. The following is one solution.
class Car
attr_reader :make, :model, :year
def initialize(make, model, year)
#make = make
#model = model
#year = year
end
def self.make_car # class method (more precisely : singleton method)
print 'How many cars do you want to create? '
array_of_cars = Array.new
num_cars = gets.to_i
num_cars.times do | i |
real_index = i + 1
puts
print "Enter make for car #{real_index}: "
make = gets.chomp
print "Enter model for car #{real_index}: "
model = gets.chomp
print "Enter year for car #{real_index}: "
year = gets.to_i
=begin
c = Car.new(make, model, year)
array_of_cars << c
=end
# some will tell you to avoid unnecessary variables ...
array_of_cars << Car.new(make, model, year)
end
puts
puts 'You have the following cars:' # sorted by year for fun
array_of_cars.sort_by{ | car | car.year }.each do | car |
puts "#{car.year} #{car.make} #{car.model}"
end
end
end # class Car
Car.make_car
I have class Actor with some attributes.
class Actor
attr_accessor :name, :age, :sex, :birth_date, :birthplace, :filmography, :death_date
def alive?
death_date.nil?
end
end
When I iterate over an array actors of Actors and display the elements as follows:
display = actors.each do |i|
puts puts i.inspect
end
I get empty lines between the items:
#<Actor:0x007f7c04da41c0 #name="Paul Newman", #age=83, #sex="M", #filmography=["Cool Hand Luke", "Butch Cassidy and the Sundance Kid"]>
#<Actor:0x007f7c04da40d0 #name="Catherine Keener", #age=52, #sex="F", #filmography=["Being John Malkovich", "Capote"], #death_date="Jan 01 2011">
#<Actor:0x007f7c04ba3c40 #name="Kathy Pornstar", #age=24, #sex="F", #filmography=["Pono", "Capote"]>
With the following code, the output does not have empty lines in between the items:
living = actors.select{ |i| "\n"; i.death_date.nil?}
puts "#{living}"
Output:
[#<Actor:0x007f7c04da41c0 #name="Paul Newman", #age=83, #sex="M", #filmography=["Cool Hand Luke", "Butch Cassidy and the Sundance Kid"]>, #<Actor:0x007f7c04ba3c40 #name="Kathy Pornstar", #age=24, #sex="F", #filmography=["Pono", "Capote"]>]
How do I make it such that the output has an empty line in between the items? Any formatting/re-formatting will be appreciated.
I assume that by "space", you mean an empty line.
The reason display has empty lines in between is because you have two puts, used in a wrong way. It would be simplified to:
display = actors.each do |i|
puts i.inspect, nil
end
and for living, you can do:
living.each do |e|
puts e.inspect, nil
end
Assuming you want to print each actor in living on a separate line, you could do the following:
living.each do |actor|
puts actor
end
Does that answer your question?
I have written down a sample program and I don't understand the following:
Why constructor without any arguments are not called in Ruby?
How do we access the class variable outside the class's definition?
Why does it always append newline characters at the end of the string? How do we strip it?
Code:
class Employee
attr_reader :empid
attr_writer :empid
attr_writer :name
def name
return #name.upcase
end
attr_accessor :salary
##employeeCount = 0
def initiaze()
##employeeCount += 1
puts ("Initialize called!")
end
def getCount
return ##employeeCount
end
end
anEmp = Employee.new
print ("Enter new employee name: ")
anEmp.name = gets()
print ("Enter #{anEmp.name}'s employee ID: ")
anEmp.empid = gets()
print ("Enter salary for #{anEmp.name}: ")
anEmp.salary = gets()
theEmpName = anEmp.name.split.join("\n")
theEmpID = anEmp.empid.split.join("\n")
theEmpSalary = anEmp.salary.split.join("\n")
anEmp = Employee.new()
anEmp = Employee.new()
theCount = anEmp.getCount
puts ("New employee #{theEmpName} with employee ID #{theEmpID} has been enrolled, welcome to hell! You have been paid as low as $ #{theEmpSalary}")
puts ("Total number of employees created = #{theCount}")
Output:
Enter new employee name: Lionel Messi
Enter LIONEL MESSI
's employee ID: 10
Enter salary for LIONEL MESSI
: 10000000
New employee LIONEL
MESSI with employee ID 10 has been enrolled, welcome to hell! You have been paid as low as $ 10000000
Total number of employees created = 0
The newlines are from the user input. When the user types something and terminates the input with a newline (enter key) the newline is seen as part of the input. You can strip it off with the String#strip() method:
empName = empName.strip
or use the in-place method:
empName.strip!
To retrieve the value of the class variable you need a static getter (note the self.):
def self.getCount
return ##employeeCount
end
Alternatively you can you the class_variable_get method.
For Question 1: Why constructor without any arguments are not called in Ruby?
You wrote def initiaze(). Correct would be def initialize():
def initialize()
##employeeCount += 1
puts ("Initialize called!")
end
as you seem to have noticed you misspelled initialize when
defining the method
you can't reference the class variable directly
outside the class, but you can make class or instance accessors for it, or use
Module#class_variable_get: Employee.class_variable_get(:##employeeCount)
gets returns the whole line the user inputs, including the terminating newline. Another answer recommended String#strip but this removes all trailing and leading whitespace. If you just want to remove the newline, use String#chomp, empName = empName.chomp! Be careful if you're tempted to apply chomp directly to gets as gets will return nil at end of file and you'll raise NoMethodError sending :chomp to nil
BTW, your camelCasedNames are not good ruby style. Constants should be all UPPER_CASE, except class and module names which should be CamelCased with leading cap, all other names should be lower_case_with_underscores_to_separate_words. Also, in ruby, one generally omits the empty parens on argumentless method calls and definitions.
Sorry about the vague question. I'm at a loss for words to describe this phenomenon, thus google wasn't much help. Please consider the following code:
array = [["name", "age"]]
a = []
x = ["Joe 32",
"Tom 45",
"Jim 36"]
x.each do |line|
name, age = line.split(/\s/)
a[0] = name
a[1] = age
array.push(a)
end
array.each do |x|
puts x.join(" ")
end
This produces:
name age
Jim 36
Jim 36
Jim 36
which is contrary to what I was expecting:
name age
Joe 32
Tom 45
Jim 36
Why is array affected after the fact by modifying a?
You want to set a to a new Array object inside the each. At the moment, you're pushing the same a object to the array, which is why it's returning the same value three times. Even better would be to not use a at all and instead convert the code into something like this:
x.each do |line|
name, age = line.split(/\s/)
array.push([name, age])
end
You could make it smaller than that even by moving the line.split to be within the push method, but I think that reduces readability and doesn't explain what information you're getting out of split.
This is slightly more advanced, but to build on Ryan's answer, rather than doing
x.each do |line|
name, age = line.split(/\s/)
array.push([name, age])
end
, you could use the map function, and have
people = x.map do |line|
name, age = line.split(/\s/)
[name, age]
end
desired_result = [["name", "age"]] + people
This is a slightly more "functional programming" approach. I'm sure this is a very rough summary, but in functional programming, you don't modify existing objects, you only create new objects instead.
As an aside, if you wanted to verify Ryan's answer, you could use object_id on each of the objects:
array.each_with_index do |object, index|
puts "Object #{index} (which is #{object.inspect}) has an object id of #{object.object_id}"
end
which gives
Object 0 (which is ["name", "age"]) has an object id of 10204144
Object 1 (which is ["Jim", "36"]) has an object id of 10248384
Object 2 (which is ["Jim", "36"]) has an object id of 10248384
Object 3 (which is ["Jim", "36"]) has an object id of 10248384