Ruby matching subsets and displaying set(variable) - ruby

feelings = Set["happy", "sad", "angry", "high", "low"]
euphoria = Set["happy", "high"]
dysphoria = Set["sad", "low"]
miserable = Set["sad", "angry"]
puts "How do you feel?"
str = gets.chomp
p terms = str.split(',')
if euphoria.proper_subset? feelings
puts "You experiencing a state of euphoria."
else
puts "Your experience is undocumented."
end
gets
How do I make euphoria a variable, such that if the corresponding string for miserable or dysphoria match & display the set name. Like #{Set}

Reviewing what you have, I think this is more like what you really want:
require 'set'
feelings = {
euphoria: Set.new(%w[happy high]),
dysphoria: Set.new(%w[sad low]),
miserable: Set.new(%w[sad angry])
}
puts "What are you feeling right now?"
mood = Set.new gets.scan(/\w+/)
name, _ = feelings.find{ |_,matches| matches.subset?( mood ) }
if name
puts "You are experiencing a state of #{name}"
else
puts "Your experience is undocumented."
end
Calling gets.scan(/\w+/) returns an array of strings. It's better than just .split(',') because it allows the user to put a space after commas (e.g. "sad, happy") or just use spaces (e.g. "sad happy").
As you already know, Set[] requires multiple arguments for it. Instead, we use Set.new which takes an array of values. Alternatively, you could have used mood = Set[*gets.scan(/\w+/)], where the * takes the array of values and passes them as explicit parameters.
Also, I changed from proper_subset? to just subset?, because "happy,high" is not a proper subset of "happy,high", but it is a subset.

Whenever you think you want to put the name of a variable into another variable, you probably want a Hash instead:
states = {
'euphoria' => Set["happy", "high"],
'dysphoria' => Set["sad", "low"],
'miserable' => Set["sad", "angry"]
}
Then you can say things like:
which = 'euphoria' # Or where ever this comes from...
if states[which].proper_subset? feelings
puts "You experiencing a state of #{which}."
else
puts "Your experience is undocumented."
end

Related

Can I recall the "case" in case?

I want to recall the case until user writes a or b. I do not want to use "case"
particularly.
I just want to get input from user but not geting something else. If he writes something else, he should need to write until he writes a or b.
str = gets.chomp.to_s
case str
when "a"
print "nice a"
when "b"
puts "nice b"
else
puts "please do it again"
end
class person
attr_accessor :name , :surname #and other attributes
end
#There will be a method here and it will run when the program is opened.
#The method will create the first object as soon as the program is opened.
#The new object that the user will enter will actually be the 2nd object.
puts "What do you want to do?
add
list
out"
process = gets.chomp.to_s
case process
when "add"
#in here user will add new objects of my class
when "list"
#in here user will show my objects
when "out"
puts "Have a nice day"
else
puts "please do it again"
end
In fact, if you look at it, many actions will be taken as a result of the user entering the correct input. what I want to tell is more detailed in this example. According to the input of the user, there will be actions such as calling methods, adding objects, etc.
I wrote most of the code on my computer. But still I couldn't solve my first problem.
Use Kernel#loop
There are a lot of ways to solve this problem, but let's start with a simple Kernel#loop wrapper around your existing code, as that's probably the easiest path forward for you.
loop do
str = gets.chomp.to_s
case str
when "a"
print "nice a"
when "b"
puts "nice b"
else
puts "please do it again"
# restart your loop when not "a" or "b"
next
end
# exit the loop if else clause wasn't triggered
break
end
Use until Control Expression
The loop construct above is pretty straightforward, but it requires you to think about where you need next and break statements for flow control. My own instinct would be to simply call a block until it's truthy. For example, the core logic could be shortened to:
str = nil; until str =~ /a|b/i do str = gets.chomp end; p str
This is a lot shorter, but it's not particularly user-friendly. To leverage this approach while making the solution more communicative and error-resistant, I'd refactor the original code this way:
# enable single-character input from console
require 'io/console'
# make sure you don't already have a value,
# especially in a REPL like irb
str = nil
until str =~ /a|b/ do
printf "\nLetter (a, b): "
str = STDIN.getch.downcase
end
puts "\nYou entered: #{str}"
While not much shorter than your original code, it handles more edge cases and avoids branching. It also seems less cluttered to me, but that's more a question of style. This approach and its semantic intent also seem more readable to me, but your mileage may legitimately vary.
See Also
IO::Console
Control Expressions
"I just want to do something until something else happens" is when you use some sort of while loop.
You can do this:
while true
str = gets.chomp
break unless str == 'a' || str == 'b'
puts "please do it again"
end
You can also use loop do:
loop do
str = gets.chomp
break unless ['a', 'b'].include?(str)
puts "please do it again"
end
puts "Nice #{str}."
Rubyists tend to prefer loop do over while true. They do pretty much the same thing.
One more thing. There's a simpler way to write out arrays of strings:
loop do
str = gets.chomp
break unless %w(a b).include?(str)
puts "please do it again"
end
puts "Nice #{str}."
It doesn't look a whole lot simpler, but if you have, say, 10 strings, it's definitely quicker to type in when you don't have to use all those quotation marks.
As your intuition was telling you, you don't need to use the case statement at all. Like trying to kill a flea with a sledgehammer. The most concise way to do your check is to check whether the input character is included in an array of the desired characters.

use ruby 'gets' in block

This code works as it should be:
puts "pick 1:"
num_1 = gets.chomp
array.detect { |k| k.id == num_1.to_i }
...
puts "pick n:"
num_n = gets.chomp
array.detect { |k| k.id == num_n.to_i }
I am not going to reuse 'num' variable anywhere else so I wanted not to assign gets to variable and use it in the block, like this:
puts "Pick 1:"
array.detect { |k| k.id == gets.chomp.to_i }
In console sometimes it works sometimes it doesn't. If it doesn't I am stuck in the function.
Is it illegal use or should I somehow wrap gets?
The two scripts have two different meanings/results.
In the first one you read a number from the input, you store it, then you compare all the items in array against that value.
In the second script, instead, as the "read from input" code is inside the block, it will be executed as part of the detect iterations. Therefore, you will be asked to input one value (the same value?) as many times as the number of items in the array.
According to what you are asking, the first one is probably the correct approach.

Not able to iterate properly over hash in my continuous input program (Ruby)

I am trying to see if I can create a list of user input requests with a hash in this program I am trying to create. Whats the best way to go about this?
print "would you like to begin a changeover? (y/n) "
input = gets.chomp
tracking = {
"start time" => input.Time.now,
"Type?" => input,
"shift?" => input,
"standard hrs?" => input,
"actual hrs?" => input,
"end time" => input = gets.chomp.Time.now
}
tracking.each do |key, value|
puts "great please answer the following: #{tracking}"
end
thanks in advance!
You have to remember that the evaluation is sequential, going from top to bottom (unless you are defining functions/methods). In your code:
You ask the user about a changeover
You get user input (say, y) into the variable input
You make a hash, with six values; four of them will contain y, two of them will contain current time
You iterate over the hash, printing its values (and asking the user nothing).
Or at least it would if gets.chomp.Time.now was not an error.
So, taking care about the timing:
print "would you like to begin a changeover? (y/n) "
unless gets.chomp.tolower == 'n'
puts "great please answer the following:"
tracking = {
"start time" => Time.now
}
[
"Type?",
"shift?",
"standard hrs?",
"actual hrs?"
].each do |question|
print question
tracking[question] = gets.chomp
}
tracking["end_time"] = Time.now
end
Thanks Alot! This set me on the right track. However, was not time stamping the beginning and end of the questionnaire the way I wanted. After playing with the code a bit on my own however, I was able to make it work.
print "would you like to begin a changeover? (y/n) "
unless gets.chomp. == "n"
puts Time.now
puts "great please answer the following: "
end
questionnaire = ["Type", "Shift?", "Standard hrs?", "Actual hrs?"]
.each do |question|
puts question
questionnaire = gets.chomp
end
puts "Please type 'done' when change over complete"
input = gets.chomp
puts Time.now

Variables not matching in if/else statement

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.

How do I remove unwanted from an array?

Ok so I have an array that looks like this.
["Enter Sandman", "One", "Nothing Else Matters", "Master of Puppets", "The Unforgiven", "The Day That Never Comes", "For Whom the Bell Tolls", "Fade to Black", "Sad But True", "Wherever I May Roam", "Turn the Page", "I Disappear", "Fuel", "Cyanide", "Seek & Destroy", "Whiskey In the Jar", "All Nightmare Long", "Battery", "Welcome Home (Sanitarium)", "The Unforgiven III", "The Unforgiven II", "King Nothing", "Ride the Lightning", "No Leaf Clover", "Until It Sleeps", "...And Justice for All", "Blackened", "The Memory Remains", "Hero of the Day", "The Four Horsemen", "Orion", "Creeping Death", "St. Anger", "Harvester of Sorrow", "Don't Tread on Me", "Broken, Beat & Scarred", "Disposable Heroes", "Fight Fire With Fire", "The End of the Line", "Trapped Under Ice", "Of Wolf and Man", "Whiplash", "My Apocalypse", "Suicide & Redemption", "The Shortest Straw", "Tuesday's Gone"]
This array is generated by this command
artists = search_object.map{|x| x["trackName"]}.uniq.delete_if {|x| x == nil}
this works well but I need to filter out some more elements. The user will type in the textfield and as they type i need to narrow the results. So for example if the user types the string "Matters" i need to take out the elements that dont have that in the name or like the name. So it narraws down to "Nothing Else Matters". If the user enters the letter "a" then all the others in the array that dont have an "a" get deleted.
they will come in with the params[:text]
I did this and it worked but maybe there is a cleaner way
query = params[:term]
artists = search_object.map{|x| x["trackName"]}.uniq.delete_if {|x| x == nil}
filtered = []
artists.each do |artist|
filtered << artist if artist.include?(query)
end
the fast ruby variant is:
albums = ["hello kitty", "bad day", "all is good", "day is okay"]
def filter_word_in(word,array)
array.delete_if { |data| !data.match(word) }
return array
end
result1 = filter_word_in("y", albums)
puts result1.inspect # => ["hello kitty", "bad day", "day is okay"]
result2 = filter_word_in("ay", result1)
puts result2.inspect # => ["bad day", "day is okay"]
result3 = filter_word_in("day", result2)
puts result3.inspect # => ["bad day", "day is okay"]
result4 = filter_word_in("day i",result3)
puts result4.inspect # => ["day is okay"]
How you can see in this code: we just save our result in variables.
So, where we can store our data in rails?
You can use user_model for this, or you can just store this data in memory.
Create something like this:
class UserSongMemory
attr_accessor :memory
def initialize
#memory = []
end
def push(id, data)
#memory << {id => data}
end
def pop(id)
#memory.delete_if {|obj| obj.id == id}
end
end
user_memory = UserSongMemory.new
user_memory.add(#user.id, params[:inputed_string])
# after our calculations
user.pop(#user.id)
I prefer to store state memory in Class, but don't forget to clean this class-data
I would do this:
term, fname = params[:term], "trackName"
filtered = search_object.map {|x| x[fname] if x[fname].match(term) }.compact.uniq
This approach eliminates the need for two loops, one for collection and other selection. The uniq and compact methods are there as per your requirement.
The Array class offers you the "reject" method to remove unwanted elements from your array.
Here's a slightly different approach, and my reasoning.
Reason:
I'm imagining a list on a webpage with a textbox that allows you to type a substring of your desired selection to filter down to that item. As such, my assumption is that your users will ONLY ever type a substring. These aren't power users who will be regex matching for their desired track. By just using select and include? you can limit that regex power, as people suggested, without the added complexity. Regex is a bit like using a machine gun to kill a fly in this example.
Code:
#I renamed the array, since it was a list of songs, not artists
songs.select {|s| s.upcase.include?(query.upcase)}
You could leave the upcase off if you want the query to be case sensitive

Resources