Ruby "#<NoMethodError: undefined method `+' for nil:NilClass>" - ruby

I have the following code which looks for the letters "u"and "e" in a word and add 1 to the index so that it shows position in the word starting from 1 rather than 0, and then have it combined into an array.
def vowel_indices(word)
x = (word.index("u") + 1)
y = (word.index("e") + 1)
print = [x,y]
end
I am getting the following error message when running:
#<NoMethodError: undefined method `+' for nil:NilClass>
What is nil in this? From what I can see my variables are assigned correctly.

"What is nil in this?"
As # Cary Swoveland and #Lahiru already said, if a word is passed in that doesn't have a 'u' or 'e' that exception will be raised:
001 > def vowel_indices(word)
002?> x = (word.index("u") + 1) #word.index("u") returns nil if the word arg passed in doesn't have a 'u'
003?> y = (word.index("e") + 1)
004?> print = [x,y] #I've never seen the 'print =' syntax...any reason you're not just returning [x,y] here?
005?> end
=> :vowel_indices
006 > vowel_indices("cruel")
=> [3, 4]
007 > vowel_indices("cat")
NoMethodError: undefined method `+' for nil:NilClass
from (irb):2:in `vowel_indices' # This tells you exactly where the exception is coming from
from (irb):7
from /Users/kenenitz/.rvm/rubies/ruby-2.2.0/bin/irb:11:in `<main>'
Two quick & dirty ways to handle this would be to either add a conditional to check for the presence of each letter, or you can rescue NoMethodError:
#conditional check (preferred as it is more precise)
def vowel_indices(word)
u_index = word.index("u")
e_index = word.index("e")
x = u_index ? u_index + 1 : nil #verify that u_index is not nil before calling :+ method
y = e_index ? e_index + 1 : nil # same for e_index
[x,y]
end
#rescuing NoMethodError, not preferred in this case but including as a possible solution just so that you're familiar w/ this approach
def vowel_indices(word)
begin
x = word.index("u") + 1
y = word.index("e") + 1
rescue NoMethodError
x = nil
y = nil
end
[x,y]
end
With either solution I provide, if a word is missing a 'u' or 'e', the return value would contain nil which would most likely require some sort of special handling elsewhere in your program:
vowel_indices("cat")
=> [nil, nil]
vowel_indices("cut")
=> [2, nil]

It's because for some values assigned as word may not contain a u or e.
If you are using Rails, then you can overcome this by modifying your code like this:
def vowel_indices(word)
x = (word.try(:index, "u").try(:+, 1)
y = (word.try(:index, "e").try(:+, 1)
print = [x,y]
end
In this way, you can prevent the method trying to call :+ method for if the class is Nil.
In instances which we are not sure about the input, it's better to use try.

word = "Hello" # example
def vowel_indices(word)
x = (word.index("u") + 1) # x = (nil + 1) *There is no "u" in "Hello"
y = (word.index("e") + 1) # y = (1 + 1) *Output as expected based on index
print = [x,y]
end
p word.index("u") # will return nil if you need to check the return value

Related

Why am I getting NoMethodError in this code in ruby?

So,my question was, Why am I getting NoMethodError regarding sort method in function next_bigger when being called ?
Here's the code :
class String
def sort
self.chars.sort.join
end
end
def next_bigger n
s = n.to_s
return -1 if n <= 10 || s == s.sort.reverse #This line resulting NoMethodError
(2..n).each do |p|
x = (s[0...-(p)] + s[-(p)..-1].sort).to_i
return x if x > n
end
-1
end
p next_bigger 12
You're not getting the NoMethodError on the line you think you are. It's happening 2 lines later. The other place where you call sort.
x = (s[0...-(p)] + s[-(p)..-1].sort).to_i
If you index a range of a string outside it's bounds, you get nil
""[1..-1] #=> nil
You get NoMethodError because you're calling sort on that nil.

Passing nill value to custom method

Can you please tell me why it is passing nil to check method? I am getting error main.rb:5:in `check': undefined method `%' for nil:NilClass (NoMethodError)
my_array = Array.new
$output = String.new
def check(n)
if n%3 == 0
$output = $output + 'Pop '
elsif n.even?
$output = $output + 'Crackle '
elsif n.odd?
$output = $output + 'Snap '
end
end
for x in 1..6
my_array[x] = gets.chomp.to_i
end
my_array.each { |x| check(x) }
puts $output.my_array
The reason you are getting a nil in the beginning of the array is that you are manually setting the keys in the array which creates a hole since arrays are 0 indexed in Ruby:
ary = Array.new
ary[1] = "a"
ary[2] = "b"
ary[3] = "c"
# => [nil, "a", "b", "c"]
While you could salvage this code with:
my_array = Array.new
$output = String.new
def check(n)
if n%3 == 0
$output = $output + 'Pop '
elsif n.even?
$output = $output + 'Crackle '
elsif n.odd?
$output = $output + 'Snap '
end
end
for x in 0..5
my_array[x] = gets.chomp.to_i
end
my_array.each { |x| check(x) }
puts $output.my_array
A more idiomatically correct way to write this in Ruby is:
str = 5.times.map do
n = gets.chomp.to_i
if n%3 == 0
'Pop'
elsif n.even?
'Crackle'
elsif n.odd?
'Snap'
end
end.join(" ")
puts str
for String.new and Array.new are rarely used if ever used. Use blocks instead of methods unless you're planning to reuse it later. In Ruby you can use the methods from Enumerable to both iterate over and transform arrays, hashes, ranges and other types of objects so there rarely is a reason to iterate and modify an external variable like in other languages.
With for x in 0..5 you would then have
t.rb:21:in `<main>': undefined method `my_array' for "":String (NoMethodError)
because my_array is not a method that you can send to $output.
There are many ways to do the same thing in Ruby.
my_array = []
def check(n)
case
when n % 3 == 0
'Pop'
when n.even?
'Crackle'
when n.odd?
'Snap'
else 'boom !' # not necessary in this particular case
end
end
(1..6).each do | i |
print "#{i} Enter a number > "
my_array << gets.to_i
end
puts my_array.collect { |e| check(e) }.join(' ')
Execution :
$ ruby t.rb
1 Enter a number > 44
2 Enter a number > 66
3 Enter a number > 30494
4 Enter a number > 383849
5 Enter a number > 2234
6 Enter a number > 4333
Crackle Pop Crackle Snap Crackle Snap
Don't use global variables, like $output. In the ancient (imperative programming style) languages, it was a common bug to inadvertently modify a variable accessible from anywhere.
The object oriented paradigm has been invented to isolate variables (encapsulated in an
object) to make it more difficult to modify them accidentally.
You could have use an instance variable :
#output = ''
if n%3 == 0
#output << 'Pop '
but beeing defined in the special 'main' object, it is not protected against unwanted access.
chomp is not necessary before to_i, see this post
Use iterators instead of loops. for is imperative style (C, Java), which imposes you to manage
the begin and end indexes. In an object oriented language, you simply send an iterate message to a
collection object, which takes cares of the internal details.
if and case are expressions which return the last computed value. check() returns that value.
Your my_array.each { |x| check(x) } mutates the variable $output and returns no result. In a big program, a later maintenance could insert some processing that modifies $output before you use it (bug).
The functional programming paradigm (Scala, Elixir, Kotlin) tends to use immutable variables and use functions to transform data.
The new my_array.collect { |e| check(e) }.join(' ') iterates over my_array, transforms each element calling the function check(), produces a new (immutable) collection with these transformed elements, which is then transformed by the function join() to produce the final result.
You have
for x in 1..6
my_array[x] = gets.chomp.to_i
end
Which populates the array from indexes 1 through 6, all arrays begin at index 0 so, in your method
my_array.each { |x| check(x) }
The .each method will iterate through each element of the array, starting at 0, which in this case would be nil because you never assigned a value to that index, you could change your range to
for x in 0..6
my_array[x] = gets.chomp.to_i
end
And that would work, remember to use 2 dots and not 3, as
0..6
0...6
are different, the first one is inclusive, the second one is exclusive.
You can check up more about ranges here

how to convert "+=" to operator in ruby

I can convert "+" , "-" or "/" to operator use 2.send("-",3)
but it does not work with "+="
a = 2
a += 2 #=> 4
a = 2
a.send("+=", 4) #=> NoMethodError: undefined method `+=' for 2:Fixnum
I have tried to convert symbol first ; but not works too;
how to fix this?
2.send("-", 3) works, because - is a method and 2 responds to that method:
2.respond_to?('-') #=> true
= and += on the other hand are not methods:
2.respond_to?('=') #=> false
2.respond_to?('+=') #=> false
And even if = was a valid method1, then
a = 2
a.send("=", 4)
would be equivalent to:
2.send("=", 4)
or simply:
2 = 4
In other words: it would redefine 2 as 4, something Ruby doesn't allow you to do.
This is because variables (like a above) are not objects. a.send doesn't send a message to the variable a, but to the object, a is referring to (2 in the example).
The same applies to abbreviated assignment like +=:
a = 2
a += 2
is equivalent to:
a = 2
a = a + 2
You can just rewrite it as:
a = 2
a = a.send("+", 2)
The assignment is not part of the method invocation.
1 you can actually define a method = via define_method:
class Foo
define_method('=') { |other| puts "`=' called with #{other}" }
end
But it is just an ordinary method. In particular, it does not and can not alter the variable the object was assigned-to or the object's identity:
f = Foo.new
#=> #<Foo:0x007ff20c0eeda8>
f.send('=', 123)
# `=' called with 123
#=> nil
f
#=> #<Foo:0x007ff20c0eeda8>

Why isn't a variable defined by attr_accessor available in a method?

class A
attr_accessor :rank
def change_rank
rank = rank + 1
end
end
a = A.new
a.rank = 5
p a.rank
a.change_rank
p a.rank
produces an error for rank = rank + 1 (undefined method + for nil:Nilclass). Shouldn't an implicit call to the "rank" method return the value of the instance variable #rank? For some reason, this code works if I change the 4th line to:
self.rank = self.rank + 1
Why does an explicit call to the rank method works while an implicit one doesn't?
def change_rank
rank = rank + 1
end
In this context name rank does not resolve to one of methods generated by attr_accessor. It is seen by ruby roughly as this:
def change_rank
rank = nil
rank = rank + 1
end
When ruby sees an assignment to a "naked" name, it creates a local variable with this name. It will shadow the outer method. And since local vars are initialized with nil, it explains the error you're getting.
You can do this to avoid the error (be explicit about what you're modifying):
def change_rank
self.rank += 1
end
Update:
Here's more code that illustrates this
defined?(x) # => nil # name x is unidentified yet
defined?(x = x) # => "assignment" # local var x is created by assignment
defined?(x) # => "local-variable" # now local var x exists
x # => nil # and its value is nil
I rewrite your code as following. You can see how Ruby treats rank= and rank with different ways.
class A
attr_accessor :rank
def change_rank
self.rank = rank + 1
end
end
a = A.new
a.rank = 5
p a.rank
a.change_rank
p a.rank
execution result:
5
6

Is it possible to have class.property = x return something other than x?

Let's say I have a Ruby class:
class MyClass
def self.property
return "someVal"
end
def self.property=(newVal)
# do something to set "property"
success = true
return success # success is a boolean
end
end
If I try and do MyClass.property=x, the return value of the whole statement is always x. It is a convention in a lot of C-based/inspired languages to return a boolean "success" value - is it possible to do this for a setter using the "equals syntax" in Ruby?
Furthermore - if this isn't possible, why not? Is there any conceivable downside to allowing an "equals setter" operation return a value?
One downside is that you would break the chained assignment semantics:
$ irb
irb(main):001:0> x = y = 3
=> 3
irb(main):002:0> p x
3
=> nil
irb(main):003:0> p y
3
=> nil
irb(main):004:0>
Consider:
x = MyClass.property = 3
Then x would take true if this worked as you had expected (right-associativity). That could be a surprise for people using your interface and used to the typical semantics.
You also got me thinking about parallel assignment, eg:
x, y = 1, 2
Apparently the return value from that expression is implementation specific... I guess I won't be chaining parallel assignments :)
Nice question!
Like Martin says, this would break assignment chaining.
The way ruby assignment methods are defined to work expands MyClass.property = 3 to the equivalent of (lambda { |v| MyClass.send('property=', v); v })[3] (not really, but this shows how chaining works). The return value of the assignment is always the value assigned.
If you want to see the result of your MyClass#property= method, then use #send:
irb> o = Object.new
=> #<Object:0x15270>
irb> def o.x=(y)
irb> #x = y+1
irb> puts "y = #{y}, #x = ##x"
irb> true
irb> end
=> nil
irb> def o.x
irb> puts "#x = ##x"
irb> #x
irb> end
=> nil
irb> o.x = 4
y = 4, #x = 5
=> 4
irb> o.x
#x = 5
=> 5
irb> o.send('x=', 3)
y = 3, #x = 4
=> true
However, the ruby way to do this is with exceptions - if something goes wrong during
the assignment, raise an exception. Then all invokers must handle it if something goes
wrong, unlike a return value, which can be easily ignored:
# continued from above...
irb> def o.x=(y)
irb> unless y.respond_to? :> and (y > 0 rescue false)
irb> raise ArgumentError, 'new value must be > 0', caller
irb> end
irb> #x = y + 1
irb> puts "y = #{y}, #x = ##x"
irb> end
=> nil
irb> o.x = 4
y = 4, #x = 5
=> 4
irb> o.x = 0
ArgumentError: new value must be > 0
from (irb):12
from :0
irb> o.x = "3"
ArgumentError: new value must be > 0
from (irb):13
from :0
irb> o.x
#x = 5
=> 5
I'm not a Ruby expert but I'd say no for that case I'm afraid. A property setter is solely there to set the value of a private field, not to have any side effects like returning result codes.
If you want that functionality then forget the setter and write a new method called TrySetProperty or something which tries to set the property and returns a boolean.

Resources