I wrote a code where it asks multiple individuals questions, and every individuals' response is put into a hash. This is in a loop and looks something like this
arr:[]
(1..n).each do |i|
hash=Hash.new()
puts "Please input a value for day # #{i}"
hash["day1"]=gets.chomp.to_f
puts "Please input a value for day # #{i}"
hash["day2"]=gets.chomp.to_f
arr << hash
end
So to avoid any incorrect input (i.e. entering string instead of an integer/number), I need to place a conditional statement.
I am super lost with how I would do that though since I am assigning the users' input into a hash at the same time I take their input.
Is it even possible to do that or should I take a different route completely.
thanks
You can get day values like below. When a character other than a number is entered, it asks the value of that day again.
puts 'Enter number of days'
days_hash = {}
number_of_days = gets.chomp.to_i
day = 1
while day <= number_of_days
puts "Please enter a value for day # #{day}"
input = gets.chomp
if input !~ /\D/
days_hash["day#{day}"] = input.to_i
day += 1
else
puts "Please enter only number"
next
end
end
p days_hash
#=> { "day1" => 2, "day2" => 4, "day3" => 8 }
days_hash['day2']
#=> 4
Maybe you can consider this general approach for validating user input, note that this example require 'date'.
So, you can check user input to be an integer or a float or a formatted date or anything you could add...
First define an array of questions containing the question text, validation method and way to convert the input, like this:
questions = [
{text: "Enter an integer:", validate: :must_be_an_integer, convert: :my_to_i},
{text: "Enter a float:", validate: :must_be_a_float, convert: :my_to_f},
{text: "Enter a data as d-m-yyyy", validate: :must_be_a_formatted_date, convert: :to_formatted_date}
]
Then define some methods to be called by :validate key, for user input validation:
def must_be_an_integer(num)
Integer(num).class == Integer rescue false
end
def must_be_a_float(num)
Float(num).class == Float && num.include?('.') rescue false
end
def must_be_a_formatted_date(date_str)
date = Date.strptime(date_str, "%d-%m-%Y") rescue false
return false unless date
date.year <= 9999 && date.year >= 1000
end
Define also the methods required by the key :convert (need to pass an argument, that's why my_to_i and my_to_f):
def my_to_i(num)
num.to_i
end
def my_to_f(num)
num.to_f
end
def to_formatted_date(date_str)
DateTime.strptime(date_str, "%d-%m-%y")
end
Finally iterate over the questions:
res = questions.map do |question|
answer = nil
3.times do
puts question[:text]
u_input = gets.chomp
if send question[:validate], u_input
answer = send question[:convert], u_input
break
end
end
if answer.nil?
puts "C'mon man! Check your keyboard!" # after three input errors!!
break
end
{ question: question[:text], answer: answer }
end
Example of a result:
#=> [
{:question=>"Enter an integer:", :answer=>1},
{:question=>"Enter a float:", :answer=>1.1},
{:question=>"Enter a data as d-m-Y", :answer=>#<DateTime: 2020-10-27T00:00:00+00:00 ((2459150j,0s,0n),+0s,2299161j)>}
]
Related
I'm sure it would be hard to find an easier question, but I'm a complete newbie. I have searched extensively and for some reason can't find the answer to this. Here's my code:
puts "Enter F for Fahrenheit and C for Celsius."
x = gets.chomp.downcase
def ftoc(fahrenheit)
(fahrenheit.to_f - 32.0) * (5.0 / 9.0)
end
if x == "f"
puts "Enter your temp:"
temp = gets.chomp.to_i
ftoc temp
elsif x == "c"
puts "Enter your temp:"
temp = gets.chomp.to_i
ctof temp
else
puts "That does not compute."
end
I'm just trying to get the returned result of the method into a variable so I can use it elsewhere....
Remember that calls like ctof temp just initiate a method and then, as you're not putting the result anywhere, discard it immediately.
To clean up this code let's organize it better:
# Temperature conversion method
def ftoc(fahrenheit)
(fahrenheit.to_f - 32.0) * (5.0 / 9.0)
end
# User input method
def temperature_prompt!
puts "Enter F for Fahrenheit and C for Celsius."
x = gets.chomp.downcase
case (x)
when "f"
puts "Enter your temp:"
temp = gets.chomp.to_i
ftoc temp
when "c"
puts "Enter your temp:"
temp = gets.chomp.to_i
ctof temp
else
puts "That does not compute."
end
end
Now you can make use of the fact that in Ruby things like if and case actually return values. In this case it's the value of the last thing to execute in each block, so that result isn't discarded, it's just passed along:
temp = temperature_prompt!
If you enter an invalid value you get the result of puts which is conveniently nil.
Here's something to consider: Ruby is very good at parsing arbitrary text if you can describe the patterns. Here's a simple input routine:
def temperature_prompt!
puts "Enter degrees (e.g. 8F, 2C)"
case (input = gets.chomp.downcase)
when /(\d+)f/
ftoc $1
when /(\d+)c/
ctof $1
else
puts "That does not compute."
end
end
You could add to those patterns to allow things like -2C and 3.4°F if you wanted.
i have this code :
#require_relative '../lib/hackex/net/typhoeus'
require_relative '../lib/hackex'
require 'rubygems'
require 'faker'
print "how many farms do you want : "
choice = gets.chomp
choice.to_i
check = choice.is_a?(Integer)
puts check
if choice > 250
puts "Error! you cannot make more than 250 farms at once"
elsif choice < 250
puts "Error! you have to make at least one farm..."
elsif choice.is_a?(Integer) == false
puts "Error, something went wrong !"
else
puts "making #{choice} acounts ! ! !"
cnt = 1
while choice>cnt
gen = Faker::Name.first_name + Faker::Name.last_name
path=("created1.txt")
email = gen+'#gmail.com'
password = Faker::Internet.password(8)
username = gen.to_s+'/>'
puts HackEx::Request.Do(http,HackEx::Request.CreateUser(username, email, password, facebook_id = nil))
cnt +=1
open(path, 'a') { |f|
f << "#{email};"
f << "#{password}"
f << "\n"
}
puts "Account created!"
puts "#{choice - cnt} accounts remaining!"
end
end
i am trying to determing if the choice is an integer... i did the .to_i on choice, but it returns false, meaning its not an integer, its a string, why isnt it switching ?
ps : i do not get any errors, and the rest of the code works fine, except for the if part
choice.to_i returns an integer, but does not change choice. If you want choice to be changed to the integral value of the old choice, you need to reassign it explicitly:
choice = choice.to_i
Quoting the doc of String::to_i, emphasis is mine
to_i(base=10) → integer
Returns the result of interpreting leading characters in str as an
integer base base (between 2 and 36).
So you have to assign the return to something, or itself:
choice = choice.to_i
I'm create some sample code and still begineer
class Number
def initialize(name)
#first = []
#second = []
#final = 0
end
def input
print "Please input first number"
#first = gets.chomp
print "Please input second number"
#second = gets.chomp
end
def output
#final = #first * #second
puts #final
end
end
number = Number.new('Jakz')
number.class
number.input
number.output
I want to sum the 2 input number but its give error because the 2 number become a string not a number.How to fix it?
gets returns a String. The prompt does not know that you are requesting a number. Calling to_f does its best to convert the string to a floating point number
def input
print "Please input first number"
#first = gets.chomp.to_f
print "Please input second number"
#second = gets.chomp.to_f
end
be aware that if the user enters something that is not a number - the above code does not validate it, and will most probably set the variables to 0.
I am trying to solve the "24" game. The point of the game is to generate 4 random integers from 1-9 and ask the player to use addition, subtraction, multiplication, or division to get the number 24. My code runs fine until a player enters a number, and then I get "Command not found". Can someone please take a look at this:
def evaluate (input,solved_v)
input = eval (input.to_f)
#convert to a float and then evaluates it; it computes
if input == 24
solved_v = true
puts "great job! you did it!"
else
puts "please try again"
end
end
def test_entry (input)
if input.scan(%r{[^\d\s()+*/-]}).empty?
#this scan detects letters and special characters because only numbers work
true
else
false
end
end
puts
puts "try to use +, -, / or * to"
puts "get 24 from the integers provided"
puts
series = (1..4).collect{ rand(1..9)}
#generates 4 random numbers between 1 and 9
for i in series
puts i
end
puts "Please guess"
solved = false
unless solved = true
user_input = gets.chomp
if test_entry(user_input) == true
evaluate(user_input)
else
puts "invalid characters entered"
puts "please try again"
puts
end
end
There are numerous problems with your program.
Don't put spaces between your method names and parentheses.
eval takes a string argument, not a float.
Ruby passes arguments by value, so solved_v isn't going to get
returned. Make it the return value of your evaluate method. I'd also
suggest renaming your methods to express their boolean intent. See below...
Don't check boolean expressions for equality to true or false, just use them.
def correct?(input)
if eval(input) == 24
puts "great job! you did it!"
true
else
puts "please try again"
false
end
end
def good_entry?(input)
input.scan(%r{[^\d\s()+*/-]}).empty?
end
and they get used as follows
while true
user_input = gets.chomp
if good_entry?(user_input)
break if correct?(user_input)
else
...
end
end
Finally, note that you're not actually checking that the input provided by the user uses only the supplied random numbers.
I'm trying to validate that the input a user gives my program via gets is an integer. is_a?(Integer) does not work, as far as i can tell, because gets gets a string from the user, so it will always return false even if the user enters an valid integer (in string form). One would think I could simply use to_i on the input and be done with it, but that raises another issue - "75akjfas".to_i results in 75. So if I relied on to_i to solve my problems, anything starting with numbers will work.
How do I cleanly validate that the value is an integer only, and not a combination of numbers and letters? Or do I need to resort to regex for this?
print "Enter an integer (or try to sneak by something other): "
puts Integer(gets) rescue puts "Hey, that's not an integer!"
How about s.to_i.to_s == s? I'd prefer regex however.
Using regex you could do it like this:
class String
def integer?
!!(self =~ /^[-+]?[0-9]+$/)
end
end
You could use Integer() but you need to be careful with hex, binary, or octal input. http://ruby-doc.org/core-2.3.1/Kernel.html#method-i-Integer
def valid_integer?(string)
begin
!!Integer(string)
rescue ArgumentError
false
end
end
Check this code example for how to use the checked string input by the user
puts "Enter a number: "
user_input = gets.chomp
check = (user_input.to_i.to_s == user_input)
while (!check ) do
puts("Wrong Input, Pls, Enter a number: " )
user_input = gets.chomp
check = (user_input.to_i.to_s == user_input)
end
if user_input.to_i < 0
puts "Number is negative"
elsif user_input.to_i > 0
puts "Number is positve"
else
puts "Number is Zero"
end
Ruby can do it without esoteric solutions:
Integer is a integer
1970.is_a?Integer
true
String is not a integer
"1970".is_a?Integer
false
String to integer is a integer
"1970".to_i.is_a?Integer
true