Searching for a string using user input - ruby

My goal is to have the user enter a string to find a string in an array. Im using strings include? function to search but its returning the wrong data.
puts "Enter Artist(all or partial name):"
search_artist = gets.chomp
list.each do |x|
if x.artist.include? (search_artist)
num += 1
x.to_s
else
puts "none found"
end end
search_artist = 'a' (because im looking for AARON...)
returns:
AARON KDL NOT VALID 2
ZAC CHICKEN ROCK 1289
2 records found
should be:
AARON KDL NOT VALID 2
1 record found`
The problem is that both strings include 'a' somewhere in the string.
How do I search from the beginning of the string?

There's a really easy way of doing this with grep:
matches = list.grep(search_artist)
if (matches.empty?)
puts "none found"
end
To count the number of matches you can just matches.length.
If you want a case insensitive match, then you want this:
matches = list.grep(Regexp.new(search_artist, Regexp::IGNORECASE))
Where that flag creates a case-insensitive regular expression to match more broadly.
Edit: To anchor this search to the beginning of the string:
matches = list.grep(Regexp.new('\A' + Regexp.escape(search_artist), Regexp::IGNORECASE))
Where \A anchors to the beginning of the string.

Other option, just if the search is limited to the first letter, case insensitive:
found = list.select { |x| [search_artist.downcase, search_artist.upcase].include? x[0] }
found.each { |e| puts e }
puts "Found #{found.size} records"

Without Regular expressions:
puts "Enter Artist(all or partial name):"
search_artist = gets.chomp
puts list.select do |x|
x.artist.start_with?(search_artist)
end

Related

Replace specified phrase with * within text

My purpose is to accept a paragraph of text and find the specified phrase I want to REDACT, or replace.
I made a method that accepts an argument as a string of text. I break down that string into individual characters. Those characters are compared, and if they match, I replace those characters with *.
def search_redact(text)
str = ""
print "What is the word you would like to redact?"
redacted_name = gets.chomp
puts "Desired word to be REDACTED #{redacted_name}! "
#splits name to be redacted, and the text argument into char arrays
redact = redacted_name.split("")
words = text.split("")
#takes char arrays, two loops, compares each character, if they match it
#subs that character out for an asterisks
redact.each do |x|
if words.each do |y|
x == y
y.gsub!(x, '*') # sub redact char with astericks if matches words text
end # end loop for words y
end # end if statment
end # end loop for redact x
# this adds char array to a string so more readable
words.each do |z|
str += z
end
# prints it out so we can see, and returns it to method
print str
return str
end
# calling method with test case
search_redact("thisisapassword")
#current issues stands, needs to erase only if those STRING of characters are
# together and not just anywehre in the document
If I put in a phrase that shares characters with others parts of the text, for example, if I call:
search_redact("thisisapassword")
then it will replace that text too. When it accepts input from the user, I want to get rid of only the text password. But it then looks like this:
thi*i**********
Please help.
This is a classic windowing problem used to find a substring in a string. There are many ways to solve this, some that are much more efficient than others but I'm going to give you a simple one to look at that uses as much of your original code as possible:
def search_redact(text)
str = ""
print "What is the word you would like to redact?"
redacted_name = gets.chomp
puts "Desired word to be REDACTED #{redacted_name}! "
redacted_name = "password"
#splits name to be redacted, and the text argument into char arrays
redact = redacted_name.split("")
words = text.split("")
words.each.with_index do |letter, i|
# use windowing to look for exact matches
if words[i..redact.length + i] == redact
words[i..redact.length + i].each.with_index do |_, j|
# change the letter to an astrisk
words[i + j] = "*"
end
end
end
words.join
end
# calling method with test case
search_redact("thisisapassword")
The idea here is we're taking advantage of array == which allows us to say ["a", "b", "c"] == ["a", "b", "c"]. So now we just walk the input and ask does this sub array equal this other sub array. If they do match, we know we need to change the value so we loop through each element and replace it with a *.

Ruby : Replace a character in a String with its index number

I want to replace a character in string when a particular condition is satisfied. So , I went through the API doc of Ruby and found the gsub , gsub! etc for similar purpose. When I implemented that in my program I didn't got any error but din't got the desired output too.
The code which I was trying is this :
name.each_char { |c|
if name[c] == "a"
name.sub( name[c] , c )
puts "matched.... "
end
So , for example , I have a string called huzefa and want to replace all the letters with its index numbers . So , what is the way to do it ? Please explain in detail by giving a simple example.
You could pass block to gsub and do whatever you want when match happend.
To do it inplace you could use gsub! method.
name = "Amanda"
new_name = name.gsub("a") do |letter|
puts "I've met letter: " + letter
"*"
end
# I've met letter: a
# I've met letter: a
# => "Am*nd*"
If you want to work with indexes, you could do something like this:
new_name = name.chars.map.with_index do |c, i|
if i.odd?
"*"
else
c
end
end.join
#=> => "A*a*d*"
Here c and i are passed to the block. c is a character and i is an index.
if name=huzefa and you want to replace 'a' with its index..
name.split(//).map.with_index{|x,y| (x=='a')? y : x}.join
to result in #> "huzef5"

Regex to match exact word in string

I've looked around but haven't been able to find a working solution to my problem.
I have an array of two strings input and want to test which element of the array contains an exact substring Test.
One thing I have tried (among numerous other attempts):
input = ["Test's string", "Test string"]
# Alternative input array that it needs to work on:
# ["Testing string", "some Test string"]
substring = "Test"
if (input[0].match(/\b#{substring}\b/))
puts "Test 0 "
# Do something...
elsif (input[1].match(/\b#{substring}\b/))
puts "Test 1"
# Do something different...
end
The desired result is a print of "Test 1". The input can be more complex but overall I am looking for a way to find an exact match of a substring in a longer string.
I feel like this should be a rather trivial regex but I haven't been able to come up with the correct pattern. Any help would be greatly appreciated!
Following code may be what you are looking for.
input = ["Testing string", "Test string"]
substring = "Test"
if (input[0].match(/[^|\s]#{substring}[\s|$]/)
puts "Test 0 "
elsif (input[1].match(/[^|\s]#{substring}[\s|$]/)
puts "Test 1"
end
The meaning of the pattern /[^|\s]#{substring}[\s|$]/ is
[^|\s] : left side of the substring is begining of string(^) or white space,
{substring} : subsring is matched exactly,
[\s|$] : right side of the substring is white space or end of string($).
One way to that is as follows:
input = ["Testing string", "Test"]
"Test #{ input.index { |s| s[/\bTest\b/] } }"
#=> "Test 1"
input = ["Test", "Testing string"]
"Test #{ input.index { |s| s[/\bTest\b/] } }"
#=> "Test 0"
\b is the regex denotes a word boundary.
Maybe you want a method to return the index of the first element of input that contains the word? That could be:
def matching_index(input, word)
input.index { |s| s[/\b#{word}\b/i] }
end
input = ["Testing string", "Test"]
matching_index(input, "Test") #=> 1
matching_index(input, "test") #=> 1
matching_index(input, "Testing") #=> 0
matching_index(input, "Testy") #=> nil
Then you could use it like this, for example:
word = 'Test'
puts "The matching element for '#{word}' is at index #{ matching_index(input, word) }"
#=> The matching element for 'Test' is at index 1
word = "Testing"
puts "The matching element for '#{word}' is '#{ input[matching_index(input, word)] }'"
#The matching element for 'Testing' is 'Testing string'
The problem is with your bounding. In your original question, the word Test will match the first string because the ' is will match the \b word boundary. It's a perfect match and is responding with "Test 0" correctly. You need to determine how you'll terminate your search. If your input contains special characters, I don't think the regex will work properly. /\bTest my $money.*/ will never match because the of the $ in your substring.
What happens if you have multiple matches in your input array? Do you want to do something to all of them or just the first one?

Search through text word by word

I'd like to search through a txt file for a particular word. If I find that word, I'd like to retrieve the word that immediately follows it in the file. If my text file contained:
"My name is Jay and I want to go to the store"
I'd be searching for the word "want", and would want to add the word "to" to my array. I'll be looking through a very big text file, so any notes on performance would be great too.
The most literal way to read that might look like this:
a = []
str = "My name is Jack and I want to go to the store"
str.scan(/\w+/).each_cons(2) {|x, y| a << y if x == 'to'}
a
#=> ["go", "the"]
To read the file into a string use File.read.
This is one way:
Code
def find_next(fname, word)
enum = IO.foreach(fname)
loop do
e = (enum.next).scan(/\w+/)
ndx = e.index(word)
if ndx
return e[ndx+1] if ndx < e.size-1
loop do
e = enum.next
break if e =~ /\w+/
end
return e[/\w+/]
end
end
nil
end
Example
text =<<_
It was the best of times, it was the worst of times,
it was the age of wisdom, it was the age of foolishness,
. . . . .
it was the epoch of belief, it was the epoch of incredulity,
it was the season of light, it was the season of darkness,
it was the spring of hope, it was the winter of despair…
_
FName = "two_cities"
File.write(FName, text)
find_next(FName, "worst")
# of
find_next(FName, "wisdom")
# it
find_next(FName, "foolishness")
# it
find_next(FName, "dispair")
#=> nil
find_next(FName, "magpie")
#=> nil
Shorter, but less efficient, and problematic with large files:
File.read(FName)[/(?<=\b#{word}\b)\W+(\w+)/,1]
This is probably not the fastest way to do it, but something along these lines should work:
filename = "/path/to/filename"
target_word = "weasel"
next_word = ""
File.open(filename).each_line do |line|
line.split.each_with_index do |word, index|
if word == target_word
next_word = line.split[index + 1]
end
end
end
Given a File, String, or StringIO stored in file:
pattern, match = 'want', nil
catch :found do
file.each_line do |line|
line.split.each_cons(2) do |words|
if words[0] == pattern
match = words.pop
throw :found
end
end
end
end
match
#=> "to"
Note that this answer will find at most one match per file for speed, and linewise operation will save memory. If you want to find multiple matches per file, or find matches across line breaks, then this other answer is probably the way to go. YMMV.
This is the fastest I could come up with, assuming your file is loaded in a string:
word = 'want'
array = []
string.scan(/\b#{word}\b\s(\w+)/) do
array << $1
end
This will find ALL words that follow your particular word. So for example:
word = 'want'
string = 'My name is Jay and I want to go and I want a candy'
array = []
string.scan(/\b#{word}\b\s(\w+)/) do
array << $1
end
p array #=> ["to", "a"]
Testing this on my machine where I duplicated this string 500,000 times, I was able to reach 0.6 seconds execution time. I've also tried other approaches like splitting the string etc. but this was the fastest solution:
require 'benchmark'
Benchmark.bm do |bm|
bm.report do
word = 'want'
string = 'My name is Jay and I want to go and I want a candy' * 500_000
array = []
string.scan(/\b#{word}\b\s(\w+)/) do
array << $1
end
end
end

How can I stop the lines from repeating?

Look at this code. I got the desired result, which was to scan a person's input to see if it matches an internal array.
sentence = []
compare = []
database_array = ["Mouse", "killer", "Blood", "Vampires", "True Blood", "Immortal" ]
def parser_sentence compare
database_array = ["Mouse", "killer", "Blood", "Vampires", "True Blood", "Immortal"]
initial_index = 0
while compare.count > initial_index
compare.each do |item|
if item == database_array[initial_index]
puts "You found the key word, it was #{item}"
else
puts "Sorry the key word was not inside your sentence"
end
end
initial_index = initial_index + 1
end
end
puts "Please enter in your sentences of words and i will parse it for the key word."
sentence = gets.chomp
compare = sentence.split (" ")
Because each loop is telling it to repeat, it does so, but how can I stop this?
In this case, regex will be more efficient and less error prone than splitting the input string, especially since you have a two-word phrase in the keyword list.
def parser_sentence(sentence)
matching_words = sentence.scan(Regexp.union(database_array))
if matching_words.empty?
puts "Sorry the key word was not inside your sentence"
else
puts "You found the key word, it was #{matching_words.join(" ")}"
end
end
Slight modifications can make it case sensitive (if you need it), or add word boundaries to the keywords so as to not match partial words.
One possible solution that doesn't involve looping is to intersect your compare and database_array arrays, like so:
matching_words = compare & database_array
This will compare both arrays and create a new array containing only elements that are common to both. For example:
# If the user input the sentence "The Mouse is Immortal", then...
compare = ["The", "Mouse", "is", "Immortal"]
# matching_words will contain an array with elements ["Mouse", "Immortal"]
matching_words = compare & database_array
You can then check the length of the array and display out your messages. I believe this can replace your entire function like so:
def parser_sentence compare
matching_words = compare & database_array
if matching_works.length > 0
puts "You found the key word, it was #{matching_words.join(" ")}"
else
puts "Sorry the key word was not inside your sentence"
end
end
Note about the use of join, if you're unfamiliar with that, it basically creates a string using each element in the array separated by the separator string passed in, which in my example is merely a blank space; substitute for your own separate of course, or whatever you want to do with it.

Resources