Randomizing key-value pairs from a hash - ruby

I'm building a simple vocabulary quiz that provides the user with a value from a predetermined hash, and takes his or her response as input. If the user's input matches the value's corresponding key, the program moves on to the next value, and repeats this process until all key-value pairs in the hash have been accounted for.
In its current state, the quiz prompts the user with values from the hash one-by-one, in order, from first to last.
However, to make the quiz more difficult, I would like the quiz to provide RANDOM values from the hash, in no particular order.
Plain English...how do I get the vocab quiz to spit out random definitions from its library, instead of printing the same definitions in the same order every time?
My code is below. Greatly appreciate everyone's help!
vocab_words = {
"class" => "Tell Ruby to make a new type of thing",
"object" => "Two meanings: The most basic type of thing, and any instance of some thing",
"instance" => "What you get when you tell Ruby to create a class",
"def" => "How you define a function inside a class"
}
vocab_words.each do |word, definition|
print vocab_words[word] + ": "
answer = gets.to_s.chomp.downcase
while answer != "%s" %word
if answer == "help"
print "The answer is \"%s.\" Type it here: " %word
answer = gets.to_s.chomp.downcase
else
print "Nope. Try again: "
answer = gets.to_s.chomp.downcase
end
end
end

Use: random_keys = vocab_words.keys.shuffle like so:
vocab_words = {
"class" => "Tell Ruby to make a new type of thing",
"object" => "Two meanings: The most basic type of thing, and any instance of some thing",
"instance" => "What you get when you tell Ruby to create a class",
"def" => "How you define a function inside a class"
}
random_keys = vocab_words.keys.shuffle
random_keys.each do |word|
print vocab_words[word] + ": "
answer = gets.to_s.chomp.downcase
if answer == "help"
print "The answer is \"%s.\" Type it here: " %word
answer = gets.to_s.chomp.downcase
else
while answer != "%s" %word
print "Nope. Try again: "
answer = gets.to_s.chomp.downcase
end
end
end

Related

How to get key instead of key's value in a hash using Ruby and also omit the ":"

So I have an example below :
movies = {
dobby: "dobster is a lad",
pirates_of_the_carribean: "Its all about jack sparrow kicking ass!"
}
puts "what do you want to know about?\n
#{movies[:dobby]}. = 1\n
or...\n
#{movies[:pirates_of_the_carribean]}. = 2\n
Pick a number :"
Now what I get is the value of the key but I am looking to get the Key so that puts will output just the key and not the value.
I understand that the solution will likely output the key as :key and not key so id also like to know how to return a key without the ":" for displaying purposes.
Note : I have done thorough searching using google and haven't found a solution to this issue.
So I found my answer in a question that was asking something similar. To find just the key I used the .keys[] method my resulting code was this :
movies = {
dobby: "dobster is a lad",
pirates_of_the_carribean: "Its all about jack sparrow kicking ass!"
}
puts "what do you want to know about?\n
#{movies.keys[0]}. = 1\n
or...\n
#{movies.keys[1]}. = 2\n
Pick a number :"
This code prints out the keys without the : and thus for #{movies.keys[1]} it prints out pirates_of_the_carribean.
You can use the keys method in movies hash:
movies = {
dobby: "dobster is a lad",
pirates_of_the_carribean: "Its all about jack sparrow kicking ass!"
}
puts "What do you want to know about?"
counter=0
movies.keys.each do |k|
counter += 1
puts "#{k}. = #{counter} "
end
puts "Pick a number:"

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

Ruby matching subsets and displaying set(variable)

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

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