I'm trying to write a program that takes a user's input (sentence), a word the user wants to rotate around, and outputs a rotated sentence around the word that has been chosen by user.
eg. Sentence: This is a book
Word to rotate around: book
Output: book This is a
I can't seem to exit this loop of entering data (the program keeps asking for input, not doing anything more.)
Please help. Here's my code:
class SentenceRotator
def get_sentence
puts "Please enter your sentence: "
sentence = gets.chomp
get_word
end
def get_word
puts "Please enter the word you want to rotate around: "
word = gets.chomp
if converts_sentence_to_array.include?(word)
rotate_sentence_around_word
else
puts "Your word isn't in the sentence. Please enter another word."
word = gets.chomp
end
rotate_sentence_around_word
end
def converts_sentence_to_array()
get_sentence.split(" ")
end
def rotate_sentence_around_word()
new_array = converts_sentence_to_array.each_with_index {|word,index| converts_sentence_to_array.rotate(index)}
new_array
end
end
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
So, calling methods: rotate_sentence_around_word => converts_sentence_to_array => get_sentence => get_word => converts_sentence_to_array => get_sentence => ...
Try something like this:
class SentenceRotator
def gets_user_data
puts "Please enter your sentence: "
#sentence = get_sentence.split(" ")
puts "Please enter the word you want to rotate around: "
#word = get_word_to_rotate_on
end
def get_sentence
gets.chomp
end
def get_word_to_rotate_on
word = gets.chomp
return word if #sentence.include?(word)
puts "Your word isn't in the sentence. Please enter another word."
get_word_to_rotate_on
end
def rotate_sentence_around_word()
gets_user_data
#sentence.rotate(#sentence.index(#word)).join(' ')
end
end
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
Here's the logic you want, split up into separate lines:
# input
sentence = 'This is a book'
word = 'book'
# processing
words = sentence.split
pos = words.index(word)
rotated = words.rotate(pos)
back_together = rotated.join(' ')
# output
p back_together
I'd advise you to separate out your processing code as much as possible. Then you can focus on the terminal input and output logic, which is what actually seems to be your issue.
I can't seem to exit this loop of entering data (the program keeps asking for input, not doing anything more.)
It looks like your foremost problem is to take the input from user properly and enter the rotation logic. You could make use of attr_reader to access the input across methods. I have made some changes to your class to accept the input in multiple steps:
class SentenceRotator
attr_reader :sentence, :rotate_on_word
def get_sentence
puts "Please enter your sentence: "
#sentence = gets.chomp
end
def get_word_to_rotate_on
puts "Please enter the word you want to rotate around: "
#rotate_on_word = gets.chomp
unless sentence.include?(rotate_on_word)
puts "Your word isn't in the sentence. Please enter another word."
get_word_to_rotate_on
end
end
def rotate
puts sentence
puts rotate_on_word
puts 'You have all the info. Add the logic to rotate.'
end
end
> new_app = SentenceRotator.new
> new_app.get_sentence
Please enter your sentence:
This is a very funny book
> new_app
=> #<SentenceRotator:0x00007fee44178c40 #sentence="This is a very funny book">
> new_app.get_word_to_rotate_on
Please enter the word you want to rotate around:
a
> new_app
=> #<SentenceRotator:0x00007fee44178c40 #sentence="This is a very funny book", #rotate_on_word="a">
> new_app.rotate
This is a very funny book
a
You have all the info. Add the logic to rotate.
When someone tries to update a value that is not currently stored in my hash, I would like to immediately refer back to when 'add' without restarting the entire case statement since I already know they want to add and don't want to prompt them again.
Is there a way to refer back to the case choice -> when "add" section of my code without restarting the entire case statement?
I know I could use nested case statements but I would rather not copy/paste identical code if I don't have to.
hash = {}
puts "Would you like to add or update this hash?"
choice = gets.chomp
case choice
when "add"
puts "What key you like to add?"
key = gets.chomp
puts "With what value?"
value = gets.chomp
hash[key] = value
when "update"
puts "Which key would you like to update?"
key = gets.chomp
if hash[key].nil?
puts "Key not present, would you like to add it?"
#here I would like the code that references back to "when 'add'" if the user types 'yes'
Sorry for the abrupt ending of the code. I didn't want to put in anything unnecessary to the solution.
Create a method/function that wraps the functionality inside that case. Then you can call that function from both places
hash = {}
def add_key
puts "What key you like to add?"
key = gets.chomp
puts "With what value?"
value = gets.chomp
hash[key] = value
end
puts "Would you like to add or update this hash?"
choice = gets.chomp
case choice
when "add"
add_key
when "update"
puts "Which key would you like to update?"
key = gets.chomp
if hash[key].nil?
puts "Key not present, would you like to add it?"
add_key
I have done much research on this topic, but in every circumstance I attempt, the values appear to be replaced in the hash. After the person opts to enter a new ID, I would like the next person's name and age to be added to the hash. Could someone explain to me why the keys and values are being replaced?
class Person
def initialize(name, age)
if name != nil || age != nil
if #people != nil
#people[name.__id__] = age.__id__
else
#people = {name => age}
end
else
puts "Invalid credentials."
end
end
attr_reader :people
end
class MainInit
def initialize()
options = ["y", "yes", "n", "no"]
choice = "y"
while choice.downcase == "y" || choice.downcase == "yes"
p "Enter Name:"
inputname = gets.chomp
p inputname
p "Enter Age:"
inputage = gets.chomp
p inputage
person = Person.new(inputname, inputage)
p person.people
p "Enter another ID?"
choice = gets.chomp
until options.include? choice.downcase
p "Invalid Choice"
p "Enter another ID?"
choice = gets.chomp
end
end
end
end
MainInit.new
I think the reason the key-value pairs are being replaced is this:
The statement in your initialize method
if #people != nil
will always evaluate to false. initialize is called when you create a new object, so by default #people has not been defined or set yet, so each time you call
person = Person.new(inputname, inputage)
it creates a new Person rather than adding the new person to an exiting Hash (which is what I think you are trying to do).
It might work if you make people a class variable (##people), but it seems like you just want to create a Hash in your main program and then add the new entries in there.
So something like this
people = Hash.new # Or even just people = {}
Then when you have a new name / age entry to add
people[name] = age
I have not tried it, but I think your entire program should be reduced to something like this:
people = Hash.new
options = ["y", "yes", "n", "no"]
choice = "y"
while choice.downcase == "y" || choice.downcase == "yes"
p "Enter Name:"
inputname = gets.chomp
p inputname
p "Enter Age:"
inputage = gets.chomp
p inputage
#person = Person.new(inputname, inputage)
people[inputname] = inputage
person = people[inputname]
p person.people
p "Enter another ID?"
choice = gets.chomp
until options.include? choice.downcase
p "Invalid Choice"
p "Enter another ID?"
choice = gets.chomp
end
Let me both explain why you are having the problem you describe and also offer some suggestions for how you might change your code.
class Person
In class Person, you need to save your list of persons at the class level, which means the use of either a class instance variable (e.g., #people) or a class variable (e.g., ##people). I am with the majority of Rubiests in prefering the former. (The reasons are beyond the scope of this answer, but you will find a lot written on the subject by simply Googling, "Ruby 'class instance variables' versus 'class variables'". The inner quotes--the only ones you enter--help narrow the search.)
To define a class instance variable, #people, we just enter it as follows:
class Person
#people = {}
class << self
attr_accessor :people
end
def initialize(name, age)
self.class.people[name] = age
end
end
The # means it is an instance variable. As soon as Ruby reads class Person, it sets self to Person. It then reads #people = {} and makes that an instance variable of Person. By contrast, if you were to initialize #person within, say, an initialize method, self would at that time be an instance of Person, so #person would be a normal instance variable. (Aside: we could have both a class instance variable #person and an instance variable #person, and Ruby would treat them as differently as it would #night and #day.)
In order for objects to access #people we define an accessor. If we just entered attr_accessor :person, Ruby would create an accessor for a regular instance variable #person. Instead we enter class << self, which directs Ruby to associate what follows, until end is reached, with the class.
Each time a new instance of Person is created, for a given name and age,
self.class.people[name] = age
adds an element to the hash #person, since self.class is Person and people is the accessor.
Now look at the class MainInit
class MainInit
class MainInit
def initialize
loop do
name = nil
loop do
print 'Enter Name: '
name = gets.strip
break unless name.empty?
end
puts "name is #{name}"
age = nil
loop do
print 'Enter Age: '
age = gets.strip
case age
when /^\d+$/ && ('10'..'120')
break
else
puts 'age must be between 10 and 120'
end
end
puts "age is #{age}"
person = Person.new(name, age)
puts "people is now #{Person.people}"
loop do
print "Enter another ID? "
case gets.chomp.downcase
when 'n', 'no'
return
when 'y', 'yes'
break
else
puts 'Invalid choice'
end
end
end
end
end
loop do...end
You see that in several places I have used loop do...end with break to exit a loop. I'm a big fan of this construct, as compared to loop while... or or until..., in part because it avoids the need to enter a starting condition to get into the loop and then repeat the same condition withing the loop. I also just think it looks cleaner.
Any variables created within the loop cease to exist when you leave the loop, so if you want a variable's value (e.g., name and age), you must reference the variable outside of the beginning of the loops. That is why (and the only reason) I have name = nil and age = nil. It didn't have to be nil; I could have initialized them to anything.
Use of case statement
The loop for getting age uses this case statement:
case age
when /^\d+$/ && ('10'..'120')
...
end
This requires some explanation. The case statement uses String#===, rather than String#== to obtain truthy values. Therefore when /^\d+$/ is equivalent to:
/^\d+$/ === age
which is the same as
/^\d+$/ =~ age
The regex simply ensures that all characters of age are digits (e.g., "39).
Similarly,
('10'..'120') === age
is the same as
('10'..'120').cover?(age)
Odds and Ends
I used String#strip in place of String#chomp. Both remove ending newline characters, but strip also removes spaces the user may have entered at the beginning or end of the input string.
For strings, I mostly used single quotes, but double-quotes are needed for string interpolation. For example, I initially wrote puts 'name is #{name}'. That printed name is #{name}. After changing that to puts "name is #{name}", it correctly printed name is Debra.
Example
MainInit.new
Enter Name: Debra
name is Debra
Enter Age: 29
age is 29
people is now {"Debra"=>"29"}
Enter another ID? y
Enter Name: Billy-Bob
name is Billy-Bob
Enter Age: 58
age is 58
people is now {"Debra"=>"29", "Billy-Bob"=>"58"}
Enter another ID? u
Invalid choice
Enter another ID? n
I am creating a method that makes you solve random math problems. Code is below:
def subtraction()
puts "Your goal is to solve the math problem."
# Asks if user is ready
ready()
a = rand(0..5)
b = rand(0..5)
c = a - b
puts "what is #{a} - #{b}?"
prompt; next_move = gets.chomp
if next_move == c
puts "Lucky guess!"
water()
elsif next_move != c
puts "The answer was: #{c}"
dead("You suck at life")
else
dead("You didn't type anything")
end
end
I keep trying to run this and I keep getting the elsif option. Even though my variables match when I check with puts statements. I am not moving in the direction I want to. What am I doing wrong?
Change
next_move = gets.chomp
to
next_move = gets.chomp.to_i # gets.to_i will work also.
Kernel#gets will give you string, and you need to convert it to appropriate object as per your need, if your work is not with string object like this example. As per the line c = a - b, I am very much sure, you need to change your string object, that you are getting from stdin to an integer object. So you have to use String#to_i.
This question already has answers here:
Ruby: String Comparison Issues
(5 answers)
Closed 3 years ago.
I was wondering why when I'm trying to gets to different inputs that it ignores the second input that I had.
#!/usr/bin/env ruby
#-----Class Definitions----
class Animal
attr_accessor :type, :weight
end
class Dog < Animal
attr_accessor :name
def speak
puts "Woof!"
end
end
#-------------------------------
puts
puts "Hello World!"
puts
new_dog = Dog.new
print "What is the dog's new name? "
name = gets
puts
print "Would you like #{name} to speak? (y or n) "
speak_or_no = gets
while speak_or_no == 'y'
puts
puts new_dog.speak
puts
puts "Would you like #{name} to speak again? (y or n) "
speak_or_no = gets
end
puts
puts "OK..."
gets
As you can see it completely ignored my while statement.
This is a sample output.
Hello World!
What is the dog's new name? bob
Would you like bob
to speak? (y or n) y
OK...
The problem is you are getting a newline character on your input from the user. while they are entering "y" you are actually getting "y\n". You need to chomp the newline off using the "chomp" method on string to get it to work as you intend. something like:
speak_or_no = gets
speak_or_no.chomp!
while speak_or_no == "y"
#.....
end
once you use gets()...
print that string.. using p(str)
usually string will have \n at the end.. chomp! method should be used to remove it...