Ruby: skips over a gets value - ruby

I have a file check and sort script. Now I wanted the user to be to choose how he/she wishes to have the final output sorted. Sadly Ruby seems to ignore the gets command. If I comment out the entire section the script finishes just fine. Please ignore the def readout. I never finished that one....
So my question is: Why does Ruby skip over the gets command.
class Product
attr_reader :id, :name, :price, :stock
def initialize(id,name,price,stock)
#id = id
#name=name
#price=price
#stock=stock
end
def readout
self.each do |product|
print product.id
print "|"
print product.name
print "|"
print product.price
print "|"
print product.stock
puts ""
end
end
end
products = []
newproducts= []
if ARGV[0] != nil
if File.exist?(ARGV[0])
File.open(ARGV[0] , "r") do |f|
f.each_line do |line|
products << line
end
end
products.each do |product|
data = product.split(",")
newproducts.push(Product.new(data[0].strip, data[1].strip, data[2].strip.to_i, data[3].strip.to_i))
end
puts "What to sort by?"
question = gets.strip
if question == "name"
newproducts.sort! {|a,b| b.name <=> a.name}
elsif question == "price"
newproducts.sort! {|a,b| b.price <=> a.price}
elsif question =="id"
newproducts.sort! {|a,b| b.id <=> a.id}
elsif question == "stock"
newproducts.sort! {|a,b| b.stock <=> a.stock}
else
puts "Wrong Answer."
end
#End of File Check
else
puts "File #{ARGV[0]} does not exist."
end
if ARGV[1] != nil
File.open(ARGV[1], "w") do |f|
newproducts.each do |product|
puts "Added #{product.name} to the file."
data = {product.id, product.name, product.price, product.stock}
f.puts(data)
end
end
#End of ARGV check.
else
puts "No output file assigned."
end
#End of master ARGV check.
else
puts "No command given."
end

The Kernel#gets method reads from ARGF not $stdin. This means that if command line arguments were given (or more accurately if ARGV is not empty) it reads from the files in ARGV. Only otherwise does it read from stdin.
To always read from stdin, use $stdin.gets instead of gets.

Related

Why will this not iterate through the file like I want it to?

It will just output two blank lines to the screen when it should be printing the card id and the balance
I have completely re-written the code.
I have fiddled with that code for an hour
class RBC
def initialize
#args = ["Create a new card"]
#functions = ["create_rbc"]
puts "Do you have an RBC ID yet? Yes(0) No(1)"
hasrbc = gets.chomp.to_i
if hasrbc == 1
#balance = 5
create_rbc
else
login
end
end
def create_rbc
puts "\nGenerating your rbc\n\n"
puts "\nWelcome to your Ruby Binary Card(RBC)!\n\n"
puts "Your RBC will keep track of your RubyCredits(RC).\n"
puts "You will get paid RC for work apps, and pay for game apps.\n"
puts "If you lose track of your RBC ID, you can get a new one.\n"
puts "Doing this, however, will reset your balance to the default of $5\n\n"
puts "What is your name? Do first last\n"
#fullname = gets.chomp
#card_name = get_name_codec(#fullname)
#card_cipher = "#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}#{rand(1..9)}"
#card_id = "#{#card_name} - #{#card_cipher}"
instance_variable_set("#Id#{#card_cipher}", #balance)
puts "Write down your RBC ID: #{#card_id}"
file = File.open("Cards.rbc", "w")
file.puts #card_id
file.puts #balance
end
end
def get_name_codec(name)
names = name.split(" ")
fname = names[0]
lname = names[1]
fchar = fname.split(//)
fcodec = "#{fchar[0]}#{fchar[1]}"
name_codec = "#{fcodec}#{lname}"
return name_codec
end
def login
#found = false
puts "What is your RBC Id"
input = gets.chomp
File.open("Cards.RBC", "r") do |f|
f.each_line do |line|
if input == "#{line}"
#card_id = line.to_s
#found == true
elsif #found == true
#balance = line.to_i
end
end
end
puts "#{#card_id}#{#balance}"
end
RBC.new
Then the Cards.RBC
TiLan - 1122632527
5
I want it to print the balance and card Id.
It should give me my card id and then the balance like this:
0000...etc
5
input = gets.chomp removes the newline off the input, but f.each_line does not. So input == "#{line}" is comparing, for example, "1234" with "1234\n".
Chomp the line as well.
input = gets.chomp
File.open("Cards.RBC", "r") do |f|
f.each_line do |line|
line.chomp!
if input == line
#card_id = line
#found == true
elsif #found == true
#balance = line.to_i
end
end
end
You can debug this sort of thing by printing the values with p. This will show them as quoted strings and show any special characters including newlines.
p line
p input

Ruby Exceptions Initialize Error

I have to create a program where I have to ask the user for their first and last name on a single line. If the user puts only their first OR last name, it will be rejected through the exception. I keep getting an error at the end (below).
class MyNewException < Exception
attr_accessor :first, :last
def initialize (first, last)
#first = first
#last = last
end
end
print "Enter your first and last name:"
begin
first, last = gets.chomp.split
print "Hello," + first + " " + last + "!"
if last.size == 0
raise MyNewException, "Sorry, I didn't catch that! Try again:"
end
rescue MyNewException
puts "Sorry, I didn't catch that. Try again:"
retry
end
Keep getting an error:
testing.rb:15:in `+': no implicit conversion of nil into String (TypeError)
The raise() docs are faulty. You can do this:
puts "Enter your first and last name:"
name = gets.chomp
class MyNewException < Exception
def initialize(str)
super(str) #pass the value for the message property to the parent class
end
end
begin
raise MyNewException, "Sorry, I didn't quite catch that! Try again:"
rescue MyNewException => e
puts e.message
end
--output:--
Enter your first and last name:
Sarah Kim
Sorry, I didn't quite catch that! Try again:
Or:
puts "Enter your first and last name:"
name = gets.chomp
class MyNewException < Exception
attr_accessor :first, :last
def initialize(user_name, exception_message)
#first, #last = user_name.split
super exception_message
end
end
begin
raise MyNewException.new(name, "I didn't quite catch that! Try again:")
rescue MyNewException => e
puts e.message
puts e.first
puts e.last
end
--output:--
Enter your first and last name:
Sarah Kim
I didn't quite catch that! Try again:
Sarah
Kim
Here's how to write an infinite loop to get user input:
class Person
attr_accessor :first, :last
def initialize(first, last)
#first = first.capitalize
#last = last.capitalize
end
end
while true
puts "Enter your first and last name:"
first, last = gets.chomp.split
#Show what you got:
p first
p last
if first and last
#If no names were entered, both first and last will be nil.
#If one name was entered, first will evaluate to true and last will be nil.
#If two names were entered, first and last will evaluate to true.
#If two or more names were entered, first and last will evaluate to true.
user = Person.new first, last
break #jump to the line immediately after the infinite loop
end
puts "Sorry, you must enter both a first and a last name. Try again"
end
puts "Thanks #{user.first} #{user.last}!"
Create a loop that do your trial and error.
as for my answer I choose while loop
i = 0
while i < 1 do
if condition == true
# display your greetings then increment loop to end
i += 1
else
# display error message
end
end
So your code would be like this
class MyNewException < Exception
attr_accessor :first, :last, :valid
def initialize(first, last)
#first = first
#last = last
#valid = true
if first.empty? || last.empty? then
#valid = false
end
end
end
i = 0
while i < 1 do
print "Enter your first name: "
firstname = gets.chomp
print "Enter your last name: "
lastname = gets.chomp
name = MyNewException.new(firstname, lastname)
if name.valid then
puts "Greetings " << name.first << " " << name.last
i += 1
else
puts "I didn't quite catch that! Try again:"
end
end

Find the name and age of the oldest person in a txt file using ruby

"Attached is a file with people's names and ages.
There will always be a First name and Last name followed by a colon then the age.
So each line with look something like this.
FirstName LastName: Age
Your job is write a ruby program that can read this file and figure out who the oldest person/people are on this list. Your program should print out their name(s) and age(s)."
This is the code I have so far:
File.open('nameage.txt') do |f|
f.each_line do |line|
line.split(":").last.to_i
puts line.split(":").last.to_i
end
end
With this, I am able to separate the name from the age but I don't know how to get the highest value and print out the highest value with name and age.
Please help!
"figure out who the oldest person/people are on this list", so multiple results are possible. Ruby has a group_by method, which groups an enumerable by a common property. What property? The property you specify in the block.
grouped = File.open('nameage.txt') do |f|
f.group_by do |line|
line.split(":").last.to_i # using OP's line
end
end
p grouped # just to see what it looks like
puts grouped.max.last # end result
You could push all the ages into an array. Do array.max or sort the array and do array[-1].
Here's how I would approach it:
oldest_name = nil
oldest_age = 0
For each line in file do
split line at the colon and store the age inside age variable
split line at the colon and store the age inside name variable
if age is greater than oldest_age then
oldest_age = age
oldest_name = name
end
end
finally print the oldest_name and oldest_age
If you're in to one-liners try this
$ cat nameage.txt
John Doe: 34
Tom Jones: 50
Jane Doe: 32
Citizen Kane: 29
$ irb
1.9.3-p551 :001 > IO.read("nameage.txt").split("\n").sort_by { |a| a.split(":")[1].to_i }.last
=> "Tom Jones: 50"
You can try using hash also,
hash = {}
File.open('nameage.txt') do |f|
f.each_line do |line|
data = line.split(":")
hash[data.first] = data.last.strip
end
hash.max_by{|k,v| v}.join(" : ")
end
File.open('nameage.txt') do |handle|
people = handle.each_line.map { |line| line.split(":") }
oldest_age = people.map { |_, age| age.to_i }.max
people.select { |_, age| age.to_i == oldest_age }.each do |name, age|
puts "#{name}, #{age}"
end
end
You are going the right way. Now you just need to store the right things in the right places. I just merged your code and the code proposed by #oystersauce14.
oldest_name = nil
oldest_age = 0
File.open('nameage.txt') do |f|
f.each_line do |line|
data = line.split(":")
curr_name = data[0]
curr_age = data[1].strip.to_i
if (curr_age > oldest_age) then
oldest_name = curr_name
oldest_age = curr_age
end
end
end
puts "The oldest person is #{oldest_name} and he/she is #{oldest_age} years old."
Notice the use of String#strip when acquiring the age. According to the format of the file, this piece of data (the age) has a space before the first number and you need to strip this before converting it using String#to_i.
EDIT:
Since you may have more than one person with the maximum age in the list, you may do it in two passes:
oldest_age = 0
File.open('nameage.txt') do |f|
f.each_line do |line|
curr_age = line.split(":")[1].strip.to_i
if (curr_age > oldest_age) then
oldest_age = curr_age
end
end
end
oldest_people = Array.new
File.open('nameage.txt') do |f|
f.each_line do |line|
data = line.split(":")
curr_name = data[0]
curr_age = data[1].strip.to_i
oldest_people.push(curr_name) if (curr_age == oldest_age)
end
end
oldest_people.each { |person| p "#{person} is #{oldest_age}" }
I believe that now this will give you what you need.

Ruby Script Error

In the gets portion at the lower half of the program, the terminal is not asking me for input but automatically taking input. I am unable to understand how this is happenening.
The Code is:
puts "Welcome to my automatic file opener"
puts "Version - 2.0"
if ARGV[0] && ARGV[1] #to ensure Arguements are given as input
old_data = File.open(ARGV[0]).readlines
new_data = File.open(ARGV[1]).readlines
class Differentiator
def old_stuff
puts "the old files are:-"
puts old_data
end
def new_stuff
puts "The new files are:-"
puts new_data
end
def updated_list
puts "The newly added files are:-"
newly_added = new_data - old_data
puts newly_added
end
def deleted_list
puts "The deleted files are:-"
deleted_data = old_data - new_data
puts deleted_data
end
def stable_list
puts "The unchanged/stable files are:-"
unchanged_data = new_data - newly_added
puts unchanged_data
end
end#end of class
while true
puts "Choose your option:"
puts "1.Old Files of System"
puts "2.New Files of System"
puts "3.Added Files of System"
puts "4.Deleted Files of System"
puts "5.Stable Lists"
puts "6.Exit"
print " Please Choose your Output:-"
**option_method=gets.chomp.to_i**
filecase1 = Differentiator.new
if option_method == 1
filecase1.old_stuff
end
if option_method == 2
filecase1.new_stuff
end
if option_method == 3
filecase1.updated_list
end
if option_method == 4
filecase1.deleted_list
end
if option_method == 5
filecase1.stable_list
end
if option_method == 6
break
exit
end
if option_method != (1..6)
puts "Sorry,Wrong Input"
end
end
else
puts "The Right Method of Usage is : ruby <scriptname>.rb old_file new_file"; exit;
end
Because you have to use $stdin.gets otherwise it will read the files given in ARGV.
It is written in the man page http://www.ruby-doc.org/core-2.0/Kernel.html in the first line talking about gets

Sorting array with multiple variables

I am trying to create a program where by the user can enter multiple names. those names are then displayed under each other in alphabetical order, and print(display) every second name backwards. i have gone through several tutorials this is my second day using ruby.. here is what i have so far.
name_list = {}
puts 'please enter names seperated by a space:'
name_list = gets.chomp
names = name_list.split(" ")
to grab names...
names.sort do |a,b| a.upcase <=> b.upcase end
display = "#{names}"
for ss in 0...display.length
print ss, ": ", display[ss], "\n"
end
to arrange them alphabetically and under each other.
i am really struggling to mesh it all together i think i have got at least half a dozen errors in here...if i am on the wrong path could someone guide me to some info so i can start again??
EDIT
i also had this idea of using a class.
but i would have to program the names in i wanted the user to be able to add info via the consol.
class A
def initialize(name)
#name = name
end
def to_s
#name.reverse
end
end
>> a = [A.new("greg"),A.new("pete"),A.new("paul")]
>> puts a
Problems in your code:
name_list defined as an empty hash at the top but not used.
split(" ") -> split
sort { |a, b| a.method <=> b.method } -> sort_by { |x| x.method } -> sort_by(&:method)
sort is not an in-place operation, assign the result (or directly use it).
display = "#{names}" -> display = names
for ss in 0...display.length -> enumerable.each_with_index { |item, index| ... }
don't write do/end in one-liners, use { ... }
I'd write:
puts 'Please enter names separated by spaces'
gets.split.sort_by(&:upcase).each_with_index do |name, index|
puts "%s: %s" % [index, (index % 2).zero? ? name : name.reverse]
end
A few pointers then:
names.sort do |a,b| a.upcase <=> b.upcase end # Will not modify the "names" array, but will return a sorted array.
names.sort! do |a,b| a.upcase <=> b.upcase end # Will modify the "names" array.
To display your names:
names.each_with_index do |name, index|
if index % 2 == 0
puts name
else
puts name.reverse
end
end
puts 'please enter names seperated by a space`enter code here` :'
names = gets.chomp.split(" ")
names.sort! {|a,b| a.upcase <=> b.upcase } # For a single line use {..} instead of do..end
names.each_with_index do |n,i|
if i % 2 == 0
p n
else
p n.reverse
end
end
You can also use a ternary operator, I used the full if else block for readability in this case.
names.each_with_index do |n,i|
p (i % 2 == 0) ? n : n.reverse
end
EDIT
command = ""
names = []
while command != "exit"
puts 'please enter names seperated by a space`enter code here` :'
command = gets.chomp!
if command == "display"
names.sort! {|a,b| a.upcase <=> b.upcase } # For a single line use {..} instead of do..end
names.each_with_index do |n,i|
if i % 2 == 0
p n
else
p n.reverse
end
end
else
names << command
end
end

Resources