how to search user input against a list in ruby? - ruby

I need to create a simple program in ruby that will let me insert a bulk list of device IMEI numbers to check if they were already processed. Whenever I run the following code it always responds with "IMEI Not Found in database". It will reply with the last line of code always. Ive been researching for a couple days now and have decided to make a post now after being unsuccessful. Can someone please explain what I'm doing wrong?
imei_database = ["1", "2", "3"]
puts "Enter IMEIs"
imei = gets
if imei_database.include?(imei)
puts "IMEI Found in database"
else
puts "IMEI Not Found in database"
end

Problems
Your current code doesn't use String#chomp or other methods to remove the trailing carriage return from Kernel#gets. You are actually getting values like "1\n", so you won't find a match in your imei_database array.
You also haven't implemented any splitting, pattern matching, or parsing of your input values, so you currently can't process more than one item at a time. You'll need to iterate over some collection constructed from your input to do that.
Suggested Solutions
Here's some alternate code that shows one way to get the results you want, but in a more modular way. Note that the core of the heavy lifting is done with gets.chomp.split, which is a simple way to handle multi-valued input strings.
class IMEI_Status
def initialize
#imei_status = {}
imei_input
end
def report
found = 'IMEI Found in Database: %s'
not_found = 'IMEI Not Found in Database: %s'
#imei_status.map do |k, v|
p((v ? found : not_found) % k)
end
end
private
def db_lookup
# stubbed response
%w[1 2 3]
end
def imei_lookup(imei)
db_lookup.include? imei
end
def imei_input
print 'Enter space-separated IMEIs: '
gets.chomp.split.each do |imei|
#imei_status[imei] = imei_lookup(imei)
end
#imei_status
end
end
# Testing, using `1 4` as user input.
status = IMEI_Status.new
status.report
#=> ["IMEI Found in Database: 1", "IMEI Not Found in Database: 4"]
There are certainly other ways to deliver similar results, so you can adapt the approach to suit your needs.

Newline character escape required!
imei_database = ["1", "2", "3"]
puts "Enter IMEIs"
imei = gets
imei = imei.chomp
if imei_database.include?(imei)
puts "IMEI Found in database"
else
puts "IMEI Not Found in database"
end
imei = imei.gsub("\n","")
works too.

If item_database is large, many user entries are anticipated and execution speed is paramount you should use a proper database or first convert item_database to a set, assuming sufficient memory is available. Set lookups are very fast, nearly constant-time, comparable to key lookups in hashes (which is not suprising considering that sets are implemented with a hash under the covers).
require 'set'
imei_database = ["1", "2", "3"]
IMEI_DATABASE_SET = imei_database.to_set
#=> #<Set: {"1", "2", "3"}>
Then execute:
entry = gets.chomp
puts "IMEI #{entry}#{IMEI_DATABASE_SET.include?(entry) ? '' : ' Not'} Found in database"
Two examples:
entry = "2\n".chomp
#=> "2"
puts "IMEI #{entry}#{IMEI_DATABASE_SET.include?(entry) ? '' : ' Not'} Found in database"
IMEI 2 Found in database
entry = "4\n".chomp
#=> "4"
puts "IMEI #{entry}#{IMEI_DATABASE_SET.include?(entry) ? '' : ' Not'} Found in database"
IMEI 4 Not Found in database

Related

List in Ruby gem cli

I am making a ruby cli that outputs a list of game deals scraped from a site.
The list prints out promptly using
def games_sales
Deal.all.each_with_index do |deal, index|
puts "#{index + 1}. #{deal.title}"
end
puts "What game do you want to see?"
input = gets.strip
game_selection(input.to_i)
end
My problem comes when asking the user to select an item from the list.
def game_selection(input)
deal = Deal.find_by_index(input)
#binding.pry
deal.each do |deal|
puts "#{deal.index}"
puts " Name: #{deal.title}"
puts " Price: #{deal.price}"
puts " Store: #{deal.store}"
puts " Expiration: #{deal.expiration}"
end
deal
end
It returns the int input but only the first item on the list every time.
I forgot my find_by_index method:
def self.find_by_index(input)
all.select do |deal|
end
end
which is incomplete
Not 100% sure if I got your question right and if you're using Rails, but Deals.all let me think of this.
I had to replace Deals.all with DEALS for testing as I haven't got a rails app running. So I used an Array of OpenStructs to fake your Model result.
# this fakes Deals.all
require 'ostruct'
DEALS = [
# add any more properties the same way as title, separated by comma
OpenStruct.new(title: 123),
OpenStruct.new(title: 456)
]
def games_sales
DEALS.each_with_index do |deal, index|
puts "#{index + 1}. #{deal.title}"
end
puts "What game do you want to see?"
input = gets.strip
game_selection(input.to_i)
end
def game_selection(input)
deal = DEALS.at(input-1)
p deal[:title]
end
def self.find_by_index(input)
all.select do |deal|
deal.index == input
end
end
games_sales
Result when choosing 1 is 123, choosing 2 you'll get 456, due to p deal[:title] above in the code.
I think your find_by_index need to get the right index and in my example I had to use at(index) as at(input-1) in order to get the right result.
I really hope this helps somehow and I suggest that you add the expected result to your question, in case my answer does not help you.

How to save a hash in a file and use it later in ruby?

So i got in big troubles with this exam at university because i am stuck with a part in my ruby code. I just can't figure out how
" If the user presses 2 the program shall ask for an employee number and afterwards search for the employee. If the program finds it, then print and if not, print a message saying it doesn’t have it."
My problem is that i'm not sure that the information is saved corectly in the file. But if it is... the problem is that the hash i've made isn't taking the information that already is saved in the file and only works with the information it has received last.
puts "Insert Registration number \n"
search = gets.chomp
hash = Hash.new()
hash = {(regnr) => (name)}
hash.each do |key, value|
puts "#{key} \t | \t #{value}"
end
search =~ File.new("employees.txt", "r")
if hash.has_key? (search)
print "The person you were looking for is "
puts hash [search]
else
puts "He isn't one of our employees"
end
I have to tell you guys that i have only been coding for one month and the school isn't taking me easy...
I'd recommend using yaml. Take a look around the web for some examples on using YAML. It's a structured markup that can represent hashes. You can easily dump and load simple ruby objects like hashes and arrays.
require 'yaml'
parsed = begin
employee_hash = YAML.load(File.open("employees.yml"))
rescue ArgumentError => e
puts "Could not parse YAML: #{e.message}"
end

Making a sorted array of user's input

I'm learning Ruby with 'Learn to Program' by Chris Pine. On chapter 10 I should write a program where the user types as many words as he like and when he's done, he can just press Enter on an empty line and exit.
I came up with this:
puts "Type whatever you want!"
index = 0
word = ''
array = []
while word != nil
word << gets.chomp
array[index] = word
index = index + 1
end
puts ''
puts array.sort
But that doesn't work. What did I miss? Is there another way I could define word without having to repeat it?
The word will not have nil value. It will be an empty string. So you need to check for that:
while word != ""
# or even better
while !word.empty?
Also, you are adding everything to your word. You probably want to assign to it instead:
word = gets.chomp
Per author's comment:
begin
# your code here
end while !word.empty?
# OR more readable
begin
# your code here
end until word.empty?
It seems like there's a simpler solution, if I'm reading the question correctly.
You could do something like this:
user_input = gets.chomp.split(" ").sort
ex)
input: bananas clementine zebra tree house plane mine
output: ["bananas", "clementine", "house", "mine", "plane", "tree", "zebra"]
Here's a simple loop that you could do just for kicks:
arr = []
arr << $_.strip until gets =~ /^\s*$/
puts arr.sort
$_ is a special variable that evaluates to the last input read from STDIN. So basically this reads "Call gets and check if the input is just spaces. If it is then break out of the loop, otherwise append the last input with whitespace removed value onto the array and continue looping."
Or even more fun, a one liner:
puts [].tap {|arr| arr << $_.strip until gets =~ /^\s*$/}.sort
Basically same thing as above except using tap to initialize the variable.
To answer your questions:
Is there another way I could define word without having to repeat it?
Use side effects of assignment. In ruby when you assign a variable the return value of that assignment is the assigned variable, as in:
irb(main):001:0> (variable = 2) == 2
=> true
The idea would be to put the assignment in the your conditional. If I were to write something like this in a comprehensible loop, as opposed to those above, I'd write something like this:
arr = []
while !(word = gets.strip).empty?
arr << word
end
puts arr.sort
Using loop might simplify the code:
a = []
loop do
input = gets.chomp
if input.empty?
break
else
a << input
end
end
a.sort!
puts a

Combining data parsed from within the same hash in Ruby

I'm trying to combine large data sets that I've filtered out from a single hash. I've tried various things such as merge, but don't seem to be able to get the data to combine the way I'm envisioning. Here are the things I'm trying to combine:
puts '','=========GET INFO'
print_data = targetprocess.comments_with_ids #get the hash
puts print_data #show the hash for verification
puts '','=========GET IDs'
story_ids = print_data['Comments']['Comment'].map {|entry| entry['General']} #filter for story ids and story name
puts story_ids
puts '','=========GET COMMENTS'
comment_description = print_data['Comments']['Comment'].map {|words| words['Description']} #get all comments, these are in the same order as the story ids
puts comment_description
Ultimately what I would like it to look like is:
story_id 1 + comment_description 1
story_id 2 + comment_description 2
etc.
Any help would be greatly appreciated.
I ended up realizing that the hash had some other nested structures I could use. In this example I use a nested hash, then store it as an array (I ultimately need this for other work) and then output.
puts '','=========GET INFO'
print_data = targetprocess.comments_with_ids #get the hash
puts print_data #show the hash for verification
puts '=========COMPLETE', ''
#=========HASH OF USEFUL DATA
results = {}
print_data['Comments']['Comment'].each{|entry|
results[entry['Id'].chomp] = {:parent_id => entry['General']['Id'].chomp, :description => entry['Description'].chomp}}
#=========STORE HASH AS AN ARRAY
csv_array = []
results.each{|key,value|
csv_array << [key, value[:parent_id], value[:description]]
#=======FRIENDLY OUTPUT
puts "Story_Id #{value[:parent_id]}, Comment_Id #{key}, Comment #{value[:description]}"}

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

Resources