Here is the code:
def main_menu
print_main_menu
user_selected = gets.chomp
if user_selected.downcase == "no"
main_menu
elsif user_selected == "1" || "2" || "3" || "4" || "5" || "6" || "7"
user_selected = user_selected.to_i
call_option(user_selected)
else
main_menu
end
end
This code uses calls to allow a user to make a selection from a main menu. Depending on the input, be it a certain number, a certain word, or something else, the respective method is called (in the case of a valid input) or the main menu is printed again (in the case of an invalid input or "no").
My questions are twofold.
Is there an efficient way to get rid of the literal string error that appears as a result of this redundant or statement on the elsif line? (the code itself works fine, but this error appears and is frustrating).
When an alternate/unspecified input is made by the user, the else branch doesn't execute and main_method doesn't start over. I have no idea why this is happening. Is there something I'm missing here?
Thanks
Your elsif line is not semantically valid relative to your intentions. In Ruby, you can't check a variables value against several other values via x == v1 || v2 || .... You might want to consider using a Ruby case statement.
def main_menu
print_main_menu
user_selected = gets.chomp
case user_selected.downcase
when "no"
main_menu
when "1", "2", "3", "4", "5", "6", "7"
user_selected = user_selected.to_i
call_option(user_selected)
else
main_menu
end
end
It should be noted that the following expression won't do what you think it should:
elsif user_selected == "1" || "2" || "3" || "4" || "5" || "6" || "7"
This will not compare user_selected with each of the items separated by the logical OR.
The expression you are using, although not having the intent you are wanting, will not generate an error since this is a valid Ruby expression:
exp-1 || exp-2 || exp-3 || ... || exp-N
Which has the value of the first exp-i which is "truthy". That is, the first expression which doesn't evaluate to either false or nil.
So in this case, it represents a logical check of the Ruby expression:
(user_selected == "1") || "2" || "3" || "4" || "5" || "6" || "7"
The result of this expression will, as described above, be the first subexpression which is "truthy". If user_selected has the value "1", then the result will be true and the elsif will succeed. If the user_selected is not "1", then the value of the expression above will be "2" since it is "truthy", and the elsif will still succeed. In fact, it will always succeed (since "2" is always "truthy").
So your code will appear to work unless you enter something like 9 in which case the code will attempt to execute call_option(9) and perhaps generate some unexpected result depending upon what call_option does with unexpected argument values.
use case:
case user_selected
when "1", "2", "3", "4", "5", "6", "7"
user_selected = user_selected.to_i
call_option(user_selected)
else # includes "no"
main_menu
end
Related
I am having trouble using || ("or").
This is the first time I select using the "or" feature and I have been trying to select the words that are greater than 6 characters long OR start with an "e". I tried everything but I keep getting just one feature or an "and". This is the code so far
def strange_words(words)
selected_words = []
i = 0
while i < words.length
word = words[i]
if word.length < 6
selected_words << word
end
i += 1
end
return selected_words
end
print strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
puts
print strange_words(["keep", "coding"])
Using the || operator is the same as writing multiple if statements. Let's use a silly example to demonstrate it. Say you wanted to determine if a word started with the letter 'e'. Well there are a few forms of 'e'. There is the lowercase e and the upppercase E. You want to check for both forms so you could do something like this:
def starts_with_e?(string)
result = false
if string[0] == 'e'
result = true
end
if string[0] == 'E'
result = true
end
result
end
Notice however that you're doing the same actions after checking for the condition. This means you could simplify this code using the OR/|| operator, like such:
def starts_with_e?(string)
result = false
if string[0] == 'e' || string[0] == 'E'
result = true
end
end
For your specific question, you can do the following:
def strange_words(words)
words.select { |word| word.length < 6 || word[0] == 'e' }
end
When you run with your example, it gives you this output:
> strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
=> ["taco", "eggs", "we", "eatihhg", "for"]
This is still not good code. You'll want to protect the methods from bad input.
I only want to do a simple loop:
I want something like:
loop do
puts "What hotel would you like to pick"
hotelCode = gets.chomp.downcase #gets user input and puts it lowerCase
if hotelCode != "a" || hotelCode != "b" || hotelCode != "c" || hotelCode != "d" # if user input is not a,b,c,d break
break
else
puts "How many nights would you like to stay"
nights = gets.chomp.to_i
end
end #end while loop
puts "congrats u got out"
In my code, it just keeps breaking out of the loop no matter what I do. Am I missing something obvious?
Maybe you want your loops ends if the input is NONE of those choice. So
if hotelCode != "a" && hotelCode != "b" & hotelCode != "c" && hotelCode != "d"
better
if !["a", "b", "c", "d"].include?(hotelCode)
even better
if !%w(a b c d).include?(hotelCode)
or
unless %w(a b c d).include?(hotelCode)
From your code, it should be something like this:
loop do
puts "What hotel would you like to pick"
hotelCode = gets.chomp.downcase #gets user input and puts it lowerCase
if hotelCode != "a" && hotelCode != "b" && hotelCode != "c" && hotelCode != "d" # if user input is not a,b,c,d break
break
else
puts "How many nights would you like to stay"
nights = gets.chomp.to_i
end
end #end while loop
puts "congrats u got out"
if hotelCode != "a" || hotelCode != "b" || ...
If hotel code is "b", it will break on the first condition. If it is "a", it will break on the second. This condition is impossible to satisfy.
Either use
if hotelCode != "a" && hotelCode != "b" && ...
or
if hotelCode == "a" || hotelCode == "b" || ...
# handle valid hotel
else
break
end
Simple boolean math :) Or better yet, use one of Ursus' examples.
I would suggest using a method of the String class. Here are several, ordered by my personal preference (high to low).
hotel_code !~ /[abcd]/
hotel_code =~ /[^abcd]/
!"abcd".include?(hotel_code)
"abcd".index(hotel_code).nil?
hotel_code.count("abcd").zero?
hotel_code.delete("abcd") == hodel_code
"abcd".delete(hotel_code) == "abcd"
The second returns an integer ("truthy") or nil ("falsy"); the others return true or false.
I would like to loop 20 times over an if statement, but each time it's run, some things must change.
Example:
input = [0,0,0,44,754,22,0,632,2,22,0,2,nil,2,24,nil,666,90909,2,4,6,7,2,7,3,2,2,7,1,8,6,3,2,19,5,46]
Statement = "Statement THIS"
if
input[1] != nil &&
input[2] == 0
Statement.sub! "THIS", "WHERE #{input[8]} #{input[9]} THIS"
else
end
puts Statement #Statement WHERE 2 22 THIS
if
input[5] != nil &&
input[6] == 0
Statement.sub! "THIS", "AND #{input[12]} #{input[13]} THIS"
else
end
puts Statement #Statement WHERE 2 22 AND 2 THIS
if
input[9] != nil &&
input[10] == 0
Statement.sub! "THIS", "AND #{input[16]} #{input[17]} THIS"
else
end
puts Statement #Statement WHERE 2 22 AND 2 AND 666 90909 THIS
In the second IF statement the following things have changed:
Each key has increased by 4 (1,2,8,9 became 5,6,12,13)
The word WHERE has changed to AND
I would like this behaviour to repeat another 18 times, so the third IF statement has:
Each key has increased by 4 (5,6,12,13 became 9,10,16,17)
The word HELLO has changed to GOODBYE (however this rule is now redundant, since the second IF statement took care of it).
input = [
0,0,0,44,754,22,0,632,2,22,0,2,
nil,2,24,nil,666,90909,2,4,6,7,
2,7,3,2,2,7,1,8,6,3,2,19,5,46
]
(1..Float::INFINITY).step(4) do |i|
i = i.to_i # is float here
break Statement if i >= input.size
next if input[i].nil? || !input[i+1].zero?
keyword = Statement =~ /WHERE/ ? 'AND' : 'WHERE'
Statement.sub! "THIS", "#{keyword} #{input[i+7]} #{input[i+8]} THIS"
end
#⇒ "Statement WHERE 2 22 AND 2 AND 666 90909 THIS"
To loop 20 times you can use the times method.
arr_index = 1
20.times do
if arr_index < 1
operator = "WHERE"
else
operator = "AND"
end
if input[arr_index] != nil && input[arr_index + 1] == 0
Statement.sub! "THIS", "#{operator} #{input[arr_index + 7]} #{input[arr_index + 8]} THIS"
end
arr_index += 4
end
Another option would be to change the contents of your input array or create another data structure (e.g., hash, array, array of hashes, hash of arrays) with the exact values you need for the loop. Thus, eliminating the need to increment the index by 4 at each iteration.
Also, unless Stamement is a class or a constant, convention dictates its name should be lower case (e.g., statement).
Good luck!
n = 20
"STATEMENT WHERE %s THEN" % (1..1+4*(n-1)).step(4).with_object([]) { |i,a|
a << "#{ input[i+7] } #{ input[i+8] }" unless input[i].nil? || input[i+1] != 0 }.
join(" AND ")
#=> "Statement WHERE 2 22 AND 2 AND 666 90909 THEN"
This question already has answers here:
Test if variable matches any of several strings w/o long if-elsif chain, or case-when
(3 answers)
Closed 6 years ago.
I cannot get my validator to function properly.
I need to allow the user to be able to enter letters A-F (upper and lowercase) while giving them an error if they enter otherwise.
Here is my code:
print "Enter a letter A-E to add to your order "
items=gets.upcase.chomp
if items != ("A" || "B" || "C" || "D" || "E")
puts ("Incorrect Letter")
end
It functions correctly if 'A' or 'a' are entered, but it does not work at all for any of the other numbers. It seems simple enough that it should work.
Any idea what I'm doing wrong here?
if (items != "A" || items != "B" || items != "C" || items != "D" || items != "E")
is a working version with the "||".
unless items.between?("A", "E")
is perhaps more readable.
("A" || "B" || "C" || "D" || "E") is an expression that always evaluates to "A", since "A" is "truthy". Therefore, your if statement is equivalent to if items != 'A'
To check if the input is one of many options, use Array's include? function: http://ruby-doc.org/core-2.2.0/Array.html#method-i-include-3F
if ["A", "B", "C", "D", "E"].include?(items)
("A" || "B" || "C" || "D" || "E") always returns "A" in Ruby, since "A" is a not nil of false:
nil || "B"
#=> "B"
false || "B"
#=> "B"
"A" || false
#=> "A"
"A" || "B"
#=> "A"
"A" || "B" || "C"
#=> "A"
and so on.
You should use include:
unless ["A","B","C","D","E"].include?(items) puts ("Incorrect Letter") end
Since you want to include both lower- and uppercase letters, I suggest this:
unless ("a".."e").include?(items.downcase) puts ("Incorrect Letter")
Alright. I want it so the following would happen, Heres a sample:
puts "what would you like to see?"
var = gets.chomp
if var == "z"
puts "this"
elsif var == "d"
puts "stack"
elsif var == "y"
puts "overflow"
else
puts "I don't understand that."
end
Currently it works fine up until I want to have the user redefine the 'var'. Currently all the loops just loop back the puts from the elsif - i want it to ask for the user input again, then print out the elsif to that.
For example:
User types in y, the code would say overflow, then return to line 1. Any help would be awesome.
gets returns nil when there is no input, and since nil evaluates to false in a conditional, you can use gets in a loop like so
while var = gets
var.chomp!
if var == "z" # use == to test equality, not =
# do stuff
elsif var == "x"
# do stuff
....
end # while
You might find this works better with a case statement:
while var = gets
case var.chomp
when "z"
puts "this"
when "d"
puts "stack"
when "y"
puts "overflow"
else
puts "I don't understand that."
end # case
end # while
You're using the assignment operator (=) where you mean to test equality (==), I think.
You could also use case, but here's a version with while and if
print "Gimme an answer ('q' to quit) >> "
response = gets.chomp
while response != 'q'
if response == 'z'
puts "this"
elsif response == 'd'
puts "stack"
elsif response == 'y'
puts "overflow"
else
puts "I don't understand that."
end
print "Gimme another answer ('q' to quit) >> "
response = gets.chomp
end