Unable to search for a string in an Array [closed] - ruby

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
This post was edited and submitted for review last year and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
The program reads in a file of tracks and produces a Track array (tracks). When the user searches for a specific track (search_string) the program returns that it cannot be found, despite there being a track with that name in the array.
class Track
attr_accessor :name, :location
def initialize (name, location)
#name = name
#location = location
end
end
# search for track by name.
# Returns the index of the track or -1 if not found
def search_for_track_name(tracks, search_string)
search_string = gets.chomp
index = 0
while (index < tracks.length)
tracks.include?(search_string)
index = index + 1
end
if tracks.include?(search_string)
return index
else
index = -1
end
return index
end

The condition if tracks.include?(search_string) will never be true because tracks is an array of instances of Track. And therefore the array doesn't include a specific string.
It might include a track which has a matching name. To find the index of a track that has a matching name Array#index might help:
tracks.index { |track| track.name == search_string }
Because index returns nil if the value is not found you can simplify your whole if...else block and probably the whole search_for_track_name method to just this line:
tracks.index { |track| track.name == search_string } || -1

I notice your searching loop does nothing. It checks N times if the array includes the search string, ignores the result of that check, and adds one to the index. So the index is always going to be N (3 in your case). Then you check again whether the array includes the search string, and return -1 if you don't. So your method will always return either N or -1.
Fortunately, Ruby is a language which has already done a lot of the heavy lifting for you. Quite frankly I think you want:
def search_for_track_name(tracks, search_string)
tracks.index(search_string) || -1
end
#index would return nil if the search string is not found; this is more standard for Ruby, but since your requirement is to return -1, the || -1 will put it in for you. (nil and false are the only falsy values in Ruby, so || is a convenient way to shortcut to a default object if the first part is nil.)
I've also just noticed something else, and I think it might be the problem. Do you by any chance have to press Enter twice? Because the first input from read_string will get overwritten by search_string = gets.chomp. You already set search_string before you passed it to the method...
Third thing I've noticed - your track names are being populated with newlines (\n). You'll need to chomp them as well, or follow other users' advice and look for partial matches instead of exact ones.

Related

Extending (Monkey Patching) a Binary Search for Array Class and Syntactic Sugar

I've been studying a few searching algorithms and my last problem comes down to binary searching. I watched a few youtube videos to understand the concept and then tried to solve the problem, but keep getting an endless loop error. I've looked through stack overflow, and reddit, and wherever Google would lead me, but I can't quite find a solution that fits my method of coding. Also, please excuse the term 'monkey patching', it's been brought to my attention that the technical term is called 'extending' so the fault lies on my instructors for teaching it to us as 'monkey patching'.
Here's my code:
class Array
def my_bsearch(target)
return nil if self.empty?
middle_idx = self.length/2
left = self.take(middle_idx)
right = self.drop(middle_idx + 1)
return middle_idx if self[middle_idx] == target
until self[middle_idx] == target || self.nil? == nil
if self[middle_idx] < target
right.my_bsearch(target)
elsif self[middle_idx] > target
left.my_bsearch(target)
end
end
end
end
I have a solution, but I don't want to just memorize it-- and I'm having trouble understanding it; as I'm trying to translate it, learn from it, and implement what I'm missing into my own code.
class Array
def my_bsearch(target)
return nil if size == 0
mid = size/2
case self[mid] <=> target
when 0
return mid
when 1
return self.take(mid).my_bsearch(target)
else
search_res = self.drop(mid+1).my_bsearch(target)
search_res.nil? ? nil : mid + 1 + search_res
end
end
end
I guess I understand case/when despite not use to using it. I've tried following it with debugger, but I think I'm hung up on what's going on in the ELSE section. The syntactic sugar, while making this obviously more concise than my logic, isn't straight-forward/clean to someone of my ruby literacy level. So, yeah, my ignorance is most of the problem I guess.
Is there someone who is a little more literate, and patient, able to help me break this down into something I can understand a bit better so I can learn from this?
First, take and drop have sufficiently similar interfaces that you don't actually want your + 1 for drop. It will disregard one element in the array if you do.
Next, self.nil? will always be false (and never nil) for instances of this class. In fact, .nil? is a method exactly to avoid having to ever compare against nil with ==.
You want self.empty?. Furthermore, with the exception of setters, in Ruby messages are sent to self by default. In other words, the only time self. is a useful prefix is when the message ends in = and operates as an lvalue, as in self.instance_var = 'a constant', since without the self., the tokens instance_var = would be interpreted as a local variable rather than an instance variable setting. That's not the case here, so empty? will suffice just as well as self.empty?
So I figured it out, and I decided to answer my own post in hopes to help someone else out if they run into this issue.
So, if I have an Array and the target is the middle_element, then it will report middle_element_idx. That's fine. What if the target is less than middle_element? It recursively searches the left-side of the original Array. When it finds it, it reports the left_side_idx. There's no problem with that because elements in an array are sequentially counted left to right. So, it starts at 0 and goes up.
But what if the target is on the right side of the middle element?
Well, searching for the right side is easy. Relatively the same logic as searching left. Done recursively. And it will return a target_idx if it's found on that right side --however that's the target's idx as it was found in the right-side array! So, you need to take that returned target_idx and add 1 to it and the original middle_element_idx. See below:
def my_bsearch(target)
return nil if self.empty?
middle_idx = self.length/2
left = self.take(middle_idx)
right = self.drop(middle_idx + 1)
if self[middle_idx] == target
return middle_idx
elsif self[middle_idx] > target
return left.my_bsearch(target)
else
searched_right_side = 1 + right.my_bsearch(target)
return nil if searched_right_side.nil? == true
return searched_right_side + middle_idx
end
end
end
Notice how many more lines this solution is? The spaceship operator used in conjunction with case/when and a ternary method will reduce the number of lines significantly.
Based on suggestions/feedback from Tim, I updated it to:
def my_bsearch(target)
return nil if empty?
middle_idx = self.length/2
left = self.take(middle_idx)
right = self.drop(middle_idx)
if self[middle_idx] == target
return middle_idx
elsif self[middle_idx] > target
return left.my_bsearch(target)
else
searched_right_side = right.my_bsearch(target)
return nil if searched_right_side.nil?
return searched_right_side + middle_idx
end
end
end

Ruby - code to determine the most common characterS(plural) in a string [duplicate]

This question already has an answer here:
How do I count the frequencies of the letters in user input?
(1 answer)
Closed 5 years ago.
Those of you who are marking this as a duplicate - seriously, try to be more responsible. The question that you have marked as the same is quite different to the question I've asked. I've already got a great answer that was not available on any of the questions that were asked about similar topics.
My Original Question:
I see that many have asked how to find the most common letter in a string. My code for that is below. But I'd like to know how to get multiple answers to return. That is to say, if there are several letters that have the same count as most common, how do I get my method to return those only and all of them?
def ltr(string)
results = string.scan(/\w/).reduce(Hash.new(0)) {|h,c| h[c] += 1; h}
sorted = results.sort_by{|key,value| value}
sorted[-1]
end
For example, if the string I input to this method is ("oh how i hate to get up in the morning")...the there are 4 each of the letters 'h', 'o', and 't'. My current method only returns the 't' with the count of '4'. How do I get the others to be returned as well?
Note: Please read questions carefully before you decide to mark them as duplicates. The question that was suggested as a possible duplicate simply shows how to count the frequencies of characters, not how to have it return only those which are the most common. Matt's answer is perfect.
You had a good answer. Just keep going thru the resulting array keeping only the elements where the count is as high as the count of the first item:
def ltr(string)
results = string.scan(/\w/).reduce(Hash.new(0)) {|h,c| h[c] += 1; h}
sorted = results.sort_by{|key,value| value}.reverse
top = sorted[0][1]
sorted.take_while{|key,value| value == top}
end

Ruby 2D Extracting Information

I am relatively new to coding and am learning ruby right now. I came across a problem where I have a huge data record (>100k record) consisting of unique ID and another consisting of the date of birth. So it's basically a 2D array. How do I go about creating a method such that every time when I key in method(year), it will give me all the unique ID of those born in the year i choose? And how do I loop this?
The method I tried doing is as follow:
def Id_with_year(year)
emPloyee_ID_for_searching_year = [ ]
employeelist.sort_by!{|a,b|b}
if employeelist.select{|a,b| b == year}.map{|a,b| a}
return emPloyee_ID_for_searching_year
end
end
I should point out that the ID are sorted. That's why I am trying to sort the year in this method so that it will give me all the ID for the year I key in. The output I had was that it returned me [ ] with nothing inside instead of the ID.
Sidenote: methods in ruby are to be named in snake case (this is not mandatory, though.)
The problem you experience is you return what was never changed. The below should work:
def id_with_year(year)
employeelist.sort_by(&:last) # sorting by last element of array
.select{|_,b| b == year} # select
.map(&:first) # map to the first element
end

Ruby Search Array And Replace String

My question is, how can I search through an array and replace the string at the current index of the search without knowing what the indexed array string contains?
The code below will search through an ajax file hosted on the internet, it will find the inventory, go through each weapon in my inventory, adding the ID to a string (so I can check if that weapon has been checked before). Then it will add another value after that of the amount of times it occurs in the inventory, then after I have check all weapon in the inventory, it will go through the all of the IDs added to the string and display them along with the number (amount of occurrences). This is so I know how many of each weapon I have.
This is an example of what I have:
strList = ""
inventory.each do |inv|
amount = 1
exists = false
ids = strList.split(',')
ids.each do |ind|
if (inv['id'] == ind.split('/').first) then
exists = true
amount = ind.split('/').first.to_i
amount += 1
ind = "#{inv['id']}/#{amount.to_s}" # This doesn't seem work as expected.
end
end
if (exists == true) then
ids.push("#{inv['id']}/#{amount.to_s}")
strList = ids.join(",")
end
end
strList.split(",").each do |item|
puts "#{item.split('/').first} (#{item.split('/').last})"
end
Here is an idea of what code I expected (pseudo-code):
inventory = get_inventory()
drawn_inv = ""
loop.inventory do |inv|
if (inv['id'].occurred_before?)
inv['id'].count += 1
end
end loop
loop.inventory do |inv|
drawn_inv.add(inv['id'] + "/" + inv['id'].count)
end loop
loop.drawn_inv do |inv|
puts "#{inv}"
end loop
Any help on how to replace that line is appreciated!
EDIT: Sorry for not requiring more information on my code. I skipped the less important part at the bottom of the code and displayed commented code instead of actual code, I'll add that now.
EDIT #2: I'll update my description of what it does and what I'm expecting as a result.
EDIT #3: Added pseudo-code.
Thanks in advance,
SteTrezla
You want #each_with_index: http://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-each_with_index
You may also want to look at #gsub since it takes a block. You may not need to split this string into an array at all. Basically something like strList.gsub(...){ |match| #...your block }

Detecting bad syntax in a string [duplicate]

This question already has answers here:
Better way to write "matching balanced parenthesis" program in Ruby
(8 answers)
Closed 8 years ago.
I just went through those interview coding quizzes for the first time and I'm somewhere between submerging myself in a tub of dran-o and investing in No Tears bubble bath products along with a bunch of toasters.
The problem was as follows:
If you're given a string like "zx(c)abcde[z{x]}", write a function that returns true if the syntax is correct and false if the syntax is incorrect: for example, in that string the brackets and braces are messed up. In other words "{hello}mot[o]" will pass but "{hello}mo{[t}" would not.
My throught process went like: keep a list of opening and closing bracket/brace/parens positions, then see if there is overlap. But that wasn't an optimal solution so I bombed it.
I'd like some help understanding how to solve this problem.
Thanks in advance.
[Edit: I've incorporated both of #sawa's excellent suggestions.]
One way you can do this is with a stack.
MATCH = { '['=>']', '('=>')', '{'=>'}' }
OPENING = MATCH.keys
CLOSING = MATCH.values
def check_for_match(str)
str.chars.each_with_object([]) do |c, arr|
case c
when *OPENING
arr << c
when *CLOSING
return false unless c.eql?(MATCH[arr.pop])
end
end.empty?
end
check_for_match("zx(c)abcde[z{x]}") #=> false
check_for_match("zx(c)abcde[z{x}]") #=> true
[Edit: I thought this question seemed familiar. I and several others answered it a while ago.]
Another way to do this is to first strip out the irrelevant characters, then sequentially remove adjacent matching pairs until either the string is empty (return true) or the string is not empty and there are no more matching adjacent pairs (return false).
def check_for_match(str)
str = str.gsub(/[^\(\)\[\]\{\}]/, '')
while str.gsub!(/\(\)|\[\]|\{\}/, ''); end
str.empty?
end
check_for_match("zx(c)abcde[z{x]}") #=> false
check_for_match("zx(c)abcde[z{x}]") #=> true
Reader challenge: provide a proof that the syntax is incorrect when false is returned.
I would replace each bracket with an XML tag, and just run it through an XML validator. It'll pick out weird stuff like this:
<bracket>stuff<curly>morestuff</bracket></curly>
This will fail XML validation, so you can just return that.

Resources