why am I getting this error true:TrueClass (NoMethodError) - ruby

I'm trying to find a character a and then a character z 3 or less characters away from it in a string here is my code below:
def nearby_az(string)
ind1 = string.index(?a)
while ind1 != nil
return ((ind2=string.index(?z,ind1)!=nil) and ((ind2-ind1) <= 3)) #if there is a z and ind bt a and z is
#less than or equal to 3 then return true
ind1 = string.index(?a,ind1)
end
return false #can't find any a characters in the string
end
but I get this error:
07-most-letters.rb:10:in `nearby_az': undefined method `-' for true:TrueClass (NoMethodError)
from 07-most-letters.rb:20:in `<main>'
plz help

At ((ind2=string.index(?z,ind1)!=nil), you're setting ind2 to string.index(?z,ind1)!=nil, which is a boolean value. You can group ind2=string.index(?z,ind1) to avoid this:
return (((ind2=string.index(?z,ind1))!=nil) and ((ind2-ind1) <= 3))

If z must follow a, then consider this:
def nearby_az(str)
(ia = str.index('a')) && (iz = str.index('z', ia)) && iz - ia <= 3
end
nearby_az("There is alwzys the..") #=> true
nearby_az("There is always zee..") #=> false
nearby_az("There'z always the..") #=> nil
If you would like to always return true or false (and not the falsy value nil), change the operative line to:
!!((ia = str.index('a')) && (iz = str.index('z', ia)) && iz - ia <= 3)
If z could precede or follow a, then modify as follows:
def nearby_az(str)
(ia = str.index('a')) && (iz = str.index('z', [ia-3,0].max)) &&
(iz - ia).abs <= 3
end
nearby_az("There is alwzys the..") #=> true
nearby_az("There is always zee..") #=> false
nearby_az("There'z always the..") #=> true

Related

undefined method `<' for nil:NilClass error but no nil exists?

Was wondering why I get the error: "undefined method `<' for nil:NilClass" when compiling. After looking for reasons why, I found that you cannot use [] on an object with nil as a value. This makes sense, but I don't see why my array would contain nil in it. What am I missing?
def binary_search(n, arr)
middle = arr.length #pick middle value
i = 0
j = arr.length - 1
while i <= j
if arr[middle] == n
return true
elsif arr[middle] < n
i = middle + 1
middle = (i + j) / 2
else
j = middle - 1
middle = (i + j) / 2
end
end
false
end
nums = [76,32,50,90,10,8,15,49]
nums.sort
puts nums.inspect
binary_search(50, nums)
Let's look at a simplified subset of the code:
arr = [76,32,50,90,10,8,15,49]
middle = arr.length # 8
arr[middle] < 50 # NoMethodError
The length is 8.
arr[8] is nil, because there is no item at index 8. Remember that Ruby indexes begin with 0.
nil < 50 is a NoMethodError

Sorting strings in array without sorting function - Ruby

I'm trying to create a sorting algorithm without the sorting function in Ruby. I've based it of the idea of insertion sort. The idea is that the function checks whether the nth value of each two words are same, and if so n increases by one until one value is larger than the other. In that case the words might get switched. However, my function keeps freezing. Any ideas why?
words = ["my","favorite","animal", "are", "the", "elephant", "and", "the", "antelope", "and", "the", "favela"]
#Convert all letters into numbers. Output individual words as arrays.
converted_words = words.map(&:chars).map { |letters| letters.map { |letter| letter.to_i 36 } }
puts converted_words.to_s
i = 1
x = 0
while i < converted_words.length
if converted_words[i][x] == converted_words[i-1][x]
x = x + 1
else
if converted_words[i][x] < converted_words[i-1][x]
converted_words[i], converted_words[i-1] = converted_words[i-1], converted_words[i]
i = 0
x = 0
else
i = i + 1
end
end
end
puts converted_words.to_s
Your code does not "freeze"; running it raises this exception:
NoMethodError (undefined method '<' for nil:NilClass)
in the line:
if converted_words[i][x] < converted_words[i-1][x]
We immediately see the problem, though the cause is not yet known. The receiver of the method < is converted_words[i][x]. As the error message says that nil does not have a method <, we infer that converted_words[i][x] is nil.1 That means that an index is out-of-range (examples of an index being out-of-range are [1,2][412] #=> nil and [1,2][-3] #=> nil). If i were out-of-range the expression would reduce to nil[x] < ..., which would raise an exception that nil does not have a method NilClass#\[\]]. That's not our exception message so we conclude that x must be out-of-range.
To see why this is happening, suppose:
words = ["a", "ab"]
Then
converted_words =
words.map(&:chars).map { |letters| letters.map { |letter| letter.to_i 36 } }
#=> [[10], [10, 11]]
i = 1
x = 0
while i < converted_words.length
#=> while 1 < 2 => while true, so enter the loop
if converted_words[i][x] == converted_words[i-1][x]
#=> if converted_words[1][0] == converted_words[0][0] => if 10 == 10 => true
so execute
x = x + 1
#=> x = 0 + 1 => 1
and attempt to repeat the loop.
while i < converted_words.length
#=> while 1 < 2 => while true, so repeat the loop
if converted_words[i][x] == converted_words[i-1][x]
#=> if converted_words[1][1] == converted_words[0][1] => if 11 == nil => false
so execute (else).
if converted_words[i][x] < converted_words[i-1][x]
#=> converted_words[0][1] < converted_words[-1][1] => if nil < 11
#=> NoMethodError (undefined method '<' for nil:NilClass)
Error messages contain valuable information. Study them carefully!
1 The error message "nil has no method <" is here equivalent to, "NilClass has no instance method <".
I believe I have solved the issue. Thank you for all your help.
I reordered my algorithm: First checking if converted_words[i][x] < converted_words[i-1][x], and then checking if converted_words[i][x] == converted_words[i-1][x].
I also need to check whether if converted_words[i][x] != nil && converted_words[i-1][x] != nil, in order to avoid a NoMethodError (thanks Cary Swoveland).
Finally I combined the two algorithms.
I also realized that I did not need to convert the letters to numbers, as ruby knows which letters are larger. So instead, I left the characters as letters.
I'm aware that the code is not very efficent. If you have any suggestions on how to improve or simplify the algorithm, I would be happy to hear them.
Here's the code:
words = ["my","favorite","animals", "are", "the", "elephant", "and", "the", "antelope", "and", "the", "favela"]
puts words.to_s
#Convert all letters into numbers. Output individual words as arrays.
ordered_words = words.map(&:chars).map { |letters| letters.map { |letter| letter } }
i = 1
x = 0
while i < ordered_words.length
if ordered_words[i][x] != nil && ordered_words[i-1][x] != nil
if ordered_words[i][x] < ordered_words[i-1][x]
ordered_words[i], ordered_words[i-1] = ordered_words[i-1], ordered_words[i]
i = 1
x = 0
else
if ordered_words[i][x] == ordered_words[i-1][x]
x = x + 1
else
i = i + 1
x = 0
end
end
else
if ordered_words[i][x] == nil && ordered_words[i-1][x] == nil
i = i + 1
x = 0
else
if ordered_words[i][x] == nil
ordered_words[i], ordered_words[i-1] = ordered_words[i-1], ordered_words[i]
i = 1
x = 0
else
i = i + 1
x = 0
end
end
end
end
joined_words = []
ordered_words.each do |word|
joined_words.push(word.join)
end
puts joined_words.to_s

Is there a better way to compare strings in a reasonable amount of time?

I have this Ruby function that tells me if two strings are "almost" equal, that is, if all characters in the string are identical and ordered in the same way except for one. So for instance, these are equal
equal
eual
but these are not
eal
equal
(two characters are missing in the above). So with help, I have come up with this
(lcs(a,b) == shortest && longest.length - shortest.length == 1)
in which las is defined by
def lcs(xstr, ystr)
return "" if xstr.empty? || ystr.empty?
x, xs, y, ys = xstr[0..0], xstr[1..-1], ystr[0..0], ystr[1..-1]
if x == y
x + lcs(xs, ys)
else
[lcs(xstr, ys), lcs(xs, ystr)].max_by {|x| x.size}
end
end
but my function is taking an extraordinarily long time. Note my benchmark below
2.4.0 :011 > timing = Benchmark.measure { StringHelper.lcs("navesxkolsky|1227000", "navsxkolsky|1227000") }
=> #<Benchmark::Tms:0x007fa1753830d8 #label="", #real=21.341279999993276, #cstime=0.0, #cutime=0.0, #stime=0.030000000000000027, #utime=21.28, #total=21.310000000000002>
Is there something I'm missing here that can get my comparison time down to like one second instead of 21?
Try this. The main idea is that if the method is to return false, it will do so as soon as that is known, even if rudundant code is required. (The method below still works if the line return false if (sz1-sz2).abs > 1 is removed.)
def equal_but_one?(str1, str2)
sz1 = str1.size
sz2 = str2.size
return false if (sz1-sz2).abs > 1
i = [sz1, sz2].max.times.find { |i| str1[i] != str2[i] }
return false if i.nil?
case sz1 <=> sz2
when 0
str1[i+1..-1] == str2[i+1..-1]
when -1
str2[i+1..-1] == str1[i..-1]
when 1
str1[i+1..-1] == str2[i..-1]
end
end
equal_but_one?('cat', 'cut') #=> true
equal_but_one?('bates', 'bats') #=> true
equal_but_one?('buss', 'bus') #=> true
equal_but_one?('cat', 'cat') #=> false
equal_but_one?('pig', 'pigs') #=> true
equal_but_one?('pig', 'pegs') #=> false
equal_but_one?('', '') #=> false
equal_but_one?('', 'a') #=> true
require 'benchmark'
Benchmark.measure { equal_but_one?("navesxkolsky|1227000", "navsxkolsky|1227000") }.real
#=> 1.6000005416572094e-05

Counting X's and O's in a string in Ruby

I'm not sure why my code is not working, I think my logic is right?
Have the function ExOh(str) take the str parameter being passed and return the string true if there is an equal number of x's and o's, otherwise return the string false. Only these two letters will be entered in the string, no punctuation or numbers. For example: if str is "xooxxxxooxo" then the output should return false because there are 6 x's and 5 o's.
ExOh(str)
i = 0
length = str.length
count_x = 0
count_o = 0
while i < length
if str[i] == "x"
count_x += 1
elsif str[i] == "o"
count_o += 1
end
i+=1
end
if (count_o == count_x)
true
elsif (count_o != count_x)
false
end
end
The problem with your code is the function declaration. Use def ExOh(str) at the start. It may help if you indented also.
def ExOh(str)
i = 0
length = str.length
count_x = 0
count_o = 0
while i < length
if str[i] == "x"
count_x += 1
elsif str[i] == "o"
count_o += 1
end
i+=1
end
if (count_o == count_x)
true
elsif (count_o != count_x)
false
end
end
By the way, a simpler solution using the standard library #count https://ruby-doc.org/core-2.2.0/String.html#method-i-count
def ExOh(str)
str.count('x') == str.count('o')
end

Is there a Ruby shorthand for assigning a value from an array only if it is not nil?

I have the following code:
via = reminders_array[i]['value']['bot-input-keyword'] unless reminders_array[i]['value']['via'].nil?
and
collection = json_element['COLLECTION']['$'] unless json_element['COLLECTION'].nil?
When I search for examples, I saw people use ||= but I can't quite understand its use, especially in the second case.
Consider the the following statements:
e #=> NameError: undefined local variable or method `e'...
if false
e = 7
end
e #=> nil
f #=> NameError: undefined local variable or method `f'
f = 7 if false
f #=> nil
As you see, Ruby raises an exception if the value of a local variable (e or f) is sought, but if the variable first appears in a statement, but is not assigned a value, Ruby sets its value to nil. We see that:
a = b unless c.nil?
is the same as:
a = b if c
which is the same as:
a = (c ? b : nil)
which is the same as:
a = c && b
To see why the last expression is equivalent to the preceding two, let's try a couple of examples, both with:
b = 4
c = 2
c && b #=> (2 && 4) => 4
c = nil
c && b #=> (nil && 4) => nil
So I believe you want:
via = reminders_array[i]['value']['via'] &&
reminders_array[i]['value']['bot-input-keyword']
You asked about ||=, which is a form of abbreviated assignment. When Ruby's sees a ||= b she makes the assignment a = b if a is nil or false:
a || a = b
Two more examples, with b still equal to 4:
a = 2
a ||= 4 #=> a || a = 4 => a => 2 || a = 4) => 2
a = nil
a ||= 4 #=> a || a = 4 => a => nil || a = 4 #=> 4
So if a is nil, a remains nil; else a is set to b.
A common use of ||= is:
a = nil
(a ||= []) << 4
#=> [4]
a = [4]
(a ||= []) << 6
#=> [4,6]
In the first instance, a is nil, so a ||= [] converts a to an empty array, to which it appends 4. In the second case, a ([4]) is not nil, so a ||= [] leaves a unchanged ([4]). 6 is then appended to a, making a [4,6].
In addition to ||= there are:
a &&= b #=> a && a = b
a += b #=> a = a + b
a *= b #=> a = a * b
and others (see the doc). A word about a && b. If a is nil, a && b is nil. If a is not nil, a && b is b. Notice that for operators op other than || and &&, a op= b is expanded to a = a op b.

Resources