Ruby until always validates as false? - ruby

So I've written this piece of code to play around with numbers and the until loop:
number = rand(10)
puts number
puts "Guess the number"
guess = gets.chomp
until guess == number
puts "Guess again!"
guess = gets.chomp
end
puts "You've guessed it right! The number is #{guess}
But for some reason it it always stuck in the until loop and I am not sure why. I puts the random number to know that i guess right and try out the code. I'm completely new to Ruby, So I guess it's a very obvious thing I am missing, but I just dont see it.
From my point of view, whenever I prompt for the guess again, that guess that validated by the until loop with until guess == number.
Who can help me clear this up?

The reason is simple, and that is you're failing to cast the user input into an integer.
i.e. if I write
number = gets.chomp
and I type 1 for the number, then the number variable will equal the string "1", not the integer 1.
To fix this, just use guess = gets.chomp.to_i

Instead of guess = gets.chomp(which will return a string of the user input), use guess = gets.to_i (which will convert user input into integer)
to_i method will convert the element into integer and will drop /n character since it's not part of the integer. Don't need to add .chomp method.

Related

Simple program but so very stuck- Loops in Ruby

I have to write a program which asks the user to enter a number.
The program keeps on asking the user for a number until the user types 'Stop'
at which point the sum of the numbers that the user has entered should be printed.
I've tried many,many things and none of my ideas work.
This is what I have - but I can that it isn't correct. What am I doing wrong?
I've only used while loops and arrays
total_user_input = []
# As long as the user inputs a number, the program will keep putting Give me a number
# and then adding that number to the total_user_input array.
puts "Give me a number: "
while user_input = gets.chomp.to_i
#add the input to the array total_user_input
total_user_input.push(user_input.to_i)
puts "Give me a number: "
# If the user however types stop, then the loop is broken and we jump down to the
# sum bit - where all of the numbers in the total_user_input array are added together
# and printed. End of program!
if user_input == "stop"
break
end
sum = 0
total_user_input.each { |num|
sum += num
}
puts sum
end
The output isn't as it should be.
As others have identified the problems with your code let me suggest how you might reorganize it. Ruby provides many ways to execute loops but you many find it desirable to primarily relay on the method Kernel#loop and the keyword break. (As you will learn in time, loop is particularly convenient when used with enumerators.)
def sum_numbers
tot = 0
loop do
print 'Gimme a number: '
s = gets.chomp
break if s == 'Stop'
tot += s.to_i
end
tot
end
The keyword break can optionally take an argument (though why that is not mentioned in the doc I cannot say), in which case it (if a literal) or its value (if a variable or method) is returned by loop. Here one would generally see
break tot if s == 'Stop'
without the final line, tot. As the loop returns tot and that is the last calculation performed by the method, the method will return the final value of tot.
You could have instead written
return tot if user_input == 'Stop'
but I think most coders believe best practice dictates that one should not return from a method from within a loop (or from within nested loops) unless there is a good reason for doing so.
Some small points:
I used print rather than puts to that the user's entry will be shown on the same line as the prompt.
I used s (for "string") rather than user_input because it reduces the chance of spelling mistakes (e.g., user_imput), speeds reading, and (possibly a foible of mine), looks neater. True, s is not descriptive, but one only has to remember its meaning for three consecutive lines of code. Others may disagree.
You could write, break if s.downcase == 'stop' if you want, say, 'stop' or 'STOP' to have the same effect as 'Stop'.
'23O3'.to_i #=> 23 (that's an an oh, not a zero), so in real life you'd want to confirm that either 'Stop' or the string representation of a number had been typed.
This is how I would do this preferring to use loop do end syntax with a break when it should. Also added a bit more text so user knows what's happening.
total_user_input = []
puts 'Give me a number or "stop" to end: '
loop do
user_input = gets.chomp
total_user_input << user_input.to_i
puts "Give me a number: "
break if user_input.downcase == "stop"
end
puts "Total entered: #{total_user_input.inject(&:+)}" unless total_user_input.empty?
puts 'goodbye!'
Note these few things:
get.chomp.to_i will convert every input to integer. ("stop" or any non integer string will be 0)
Arrangement of the flow is quite messy.
total_user_input = []
puts "Give me a number: "
while user_input = gets.chomp.strip
total_user_input.push(user_input.to_i)
sum = 0
total_user_input.each { |num|
sum += num
}
puts sum
if user_input == "stop"
break
end
end
Hope you understand this.

Check if integer is positive in ruby

I'm trying to make users input a positive integer(>=1). If the user inputs a negative integer or 0, the program gonna notice and say you didn't put a valid integer, please try again. my program works so far until I try to input a string. My program break after I input a string. Does anybody know how to fix this?
I tried elsif !barsofsoap.match (/\A[-+]?[0-9]*.?[0-9]+\Z/)
puts "You didn't enter an integer, please put a positive integer", but it didn't work
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
x = gets.chomp
barsofsoap = Integer(x) rescue false
if barsofsoap >= 1
break
else
puts "You didn't put an integer, please put an integer and try again."
end
end
I hope the program will not break if the user inputs a string.
The reason why your current code fails is because you use false as rescue value. You can't compare false >= 1. Sticking close to your original code you might wrap the comparison into a begin/end block.
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
x = gets.chomp
begin
barsofsoap = Integer(x)
if barsofsoap >= 1
break
end
rescue ArgumentError => _error
puts "You didn't put an integer, please put an integer and try again."
end
end
Or simplifying the code:
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
break if (barsofsoap = gets.to_i).positive?
puts "Invalid input. Provide a positive integer."
end
The above code doesn't detect if the input is a string or an integer. By replacing Integer(x) with x.to_i it would simply return 0 if invalid. Since you want the user to provide an positive integer it still requests them to provide a new integer.
nil.to_i #=> 0
''.to_i #=> 0
'asdf'.to_i #=> 0
'12.34'.to_i #=> 12
Like shown above the string '12.34' will produce the value 12. Depending on your requirements you might not want to use this value and instead mark it as invalid input. In this case you could use a regex to check the provided input.
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
input = gets
break barsofsoap = input.to_i if input.match?(/\A[1-9]\d*\Z/)
puts "Invalid input. Provide a positive integer."
end
The regex /\A[1-9]\d*\Z/ will match the following:
\A Matches start of string.
[1-9] Matches one of the digits 1-9.
\d* Matches zero or more digits 0-9.
\Z Matches end of string. If the string ends with a newline, it matches just before the newline.
You try this: num.positive?
see here: https://ruby-doc.org/core-2.6/Numeric.html#method-i-positive-3F
Also you can try convert the text to an integer and validate by doing this:
num.is_a?(Integer)
https://ruby-doc.org/core-2.6/Object.html#method-i-is_a-3F
Or even better since input is always text, you can use a regex to make sure only numbers are entered: "12345" =~ /\A\d+\Z/
How about try this:
loop do
puts "How many soaps are you looking to purchase? (Please put an integer)"
barsofsoap = gets.chomp
if barsofsoap.match(/^[\d]+$/).nil?
puts "You didn't put an integer, please put an integer and try again."
elsif barsofsoap.to_i.positive?
break
else
puts "You didn't put an integer, please put an integer and try again."
end
end
Other option, using n.to_i.to_s == n to check the input is an Integer and moving the loop into a method:
def get_input_integer
loop do
puts "Enter a positive Integer"
n = gets.chomp
if n.to_i.to_s == n
return n.to_i if n.to_i.positive?
puts "It's not positive"
else
puts "It's not an integer"
end
end
end
x = get_input_integer

I'm trying to convert a value to an integer what am I doing wrong here?

I'm just trying to make this code work and I keep getting:
<=': comparison of String with 21 failed (ArgumentError)
Please tell me what I'm doing wrong.
I'm learning and I've gone through every iteration of the code I can think of to try and make it work, I'm just not sure what I've done wrong.
puts "How old are you?"
old = gets.chomp
if old <= 21
return "You are not legally allowed to buy alcohol in the US"
else
return "You are legally allowed to buy alcohol in the US"
end
I believe you have to use to_i to convert the string into an integer.
The previous answer is correct but more verbosely, here is the line you need to change in your code:
old = gets.chomp.to_i
But you might also want to make sure user only enters an integer because calling .to_i on non-numeric characters will return 0.
You might want to look at Accept only numeric input
Try/improve as needed:
input = gets.chomp
if(val = Integer(input) rescue false)
val < 21 ? 'Not old enough' : 'The usual martini?'
else
'You did not provide an age (number)'
end
It checks if the input is an Integer, so it accounts for an input like foo.
Hth...

Code should return highest of two numbers entered by the user

This is the code.
puts "Give me a number"
one = gets.chomp
puts "Give me another number"
two = gets.chomp
if one > two
puts "This is the bigger number #{one}"
else
puts "This is the bigger number #{two}"
end
I don't know where my fault is.
you didn't change them to integer, gets.chomp gives you string.
puts "Give me a number"
one = gets.chomp.to_i
puts "Give me another number"
two = gets.chomp.to_i
First, you are gathering input from an external user of your "application", you then have to decide how you want to manage wrong input and conversion.
In any case, gets will return a String
You are going for numbers, but are they Integer, Float ?
Since String is Comparable, your original code will run but will probably not yield the expected returns.
Let's say you are only interested in Integers.
You will have three conversion functions to consider :
Kernel#Integer (which is included in the top-level binding)
String#to_i.
String#to_i will never return an error.
'abcd'.to_i #=> 0
'1.2'.to_i #=> 1
nil.to_i #=> 0
Kernel#Integer will return an error if the String is not a valid Integer. (which I generally find more appropriate for user inputs).
Integer('0') #=> 0
Integer('abcd') #=> ArgumentError
Integer(nil) #=> TypeError
Finally, you will have to compare your inputs. I personnally would use Array#max since it conveys optimal readability (at the expense of performance still, but you do not seem to be in a performance critical system).
My final code would look like this (without error handling):
inputs = []
2.times do
puts 'Give me a number'
inputs << Integer(gets.chomp) # or use gets.chomp.to_i
end
puts "This is the bigger number : #{inputs.max}"
More refactoring can be done but I don't think it would serve for beginners in Ruby.

Input string does not match a numerical range

The below ruby code is not working as expected. It doesn't seem to recognise the 1..3 range and is only reading the else condition. If I gave a number instead of a range, it works though. Not sure where I'm going wrong.
print "Enter your cost: "
cost = gets.chomp
case cost
when 1..3
puts "inexpensive"
when 3..5
puts "affordable"
else puts "no comments"
end
You're trying to match a string against an integer range. That's not going to work. Make an integer.
cost = gets.chomp.to_i
The input you get from gets is always a string, so it will never match a number range. To convert it to an integer, do this:
cost = gets.to_i
You can directly put it in case statement like so
case gets.to_i

Resources