Undefined method `[]' for nil:NilClass - how to prevent?


#1

This returns correct value so long as I do not use array[0][i]. The
error is:

searchArray.rb:13: undefined method []' for nil:NilClass (NoMethodError) from shifting1.rb:7:ineach_line’
from shifting1.rb:7
Exit code: 1

#**************************************
#Searches through a text file and, at one row at a time, counts how many
occurrences of the value exist for the column. For instance, if word,
‘bird’ exists in column1 row1 then count how many birds exist for the
whole column. Next…count the occurrences of bird in the column 2. If
the numbers do not match then outputs “Value does not contain a match:”
+Value.

I am having this issue with NilClass a lot this week. What do you think
I should change?

##start code

col1 = 0
col2 = 0

File.open(‘temp.txt’, ‘r+’).each_line do |temp|
array = []
i = 0
array = temp.chomp.split(",",0)

arrayVal1 = array[0][i]
arrayVal2 = array[1][i]

 if array[0] == arrayVal1 then
   col1 = col1 + 1
  end

 if array[1] == arrayVal2 then
   col2 = col2 + 1
end
 i = i + 1

if col1 != col2 then
puts “Value does not contain a match:”
puts arrayVal1
end

end

##end code


#2

On 15.01.2009 05:51, Mmcolli00 Mom wrote:

occurrences of the value exist for the column. For instance, if word,
‘bird’ exists in column1 row1 then count how many birds exist for the
whole column. Next…count the occurrences of bird in the column 2. If
the numbers do not match then outputs “Value does not contain a match:”
+Value.

I am having this issue with NilClass a lot this week. What do you think
I should change?

The whole piece of code. No seriously, there are quite a few things to
say:

##start code

col1 = 0
col2 = 0

File.open(‘temp.txt’, ‘r+’).each_line do |temp|

The file handle is not closed properly. Rather use the block form of
File.open. Also, since you do not write the file, you can as well use

File.foreach ‘temp.txt’ do |temp|

end

Btw, the name “temp” doesn’t tell much about the contents of the
variable. I’d pick another, more meaningful name like “line”.

array = []

Superfluous initialization…

i = 0
array = temp.chomp.split(",",0)

… because here you overwrite “array” immediately without reading it.

arrayVal1 = array[0][i]
arrayVal2 = array[1][i]

 if array[0] == arrayVal1 then

As far as I can see this does not make sense: the comparison can never
be “true” because you compare an object (array[0]) with part of it
(array[0][i]).

puts arrayVal1

end

I have no idea what this is supposed to do: you compare global counts of
matches for every line. But: if there once was a mismatch in a line,
counts will not be the same in the next matching line. Is this really
what you want? Your description does not make this clear.

end

##end code

Attachments:
http://www.ruby-forum.com/attachment/3164/searchArray.rb

The whole bit certainly does not match your description. How about:

counts = values = nil

File.foreach ‘temp.txt’ do |line|
line.chomp!

better use CSV parsing:

fields = line.split ‘,’

if values
fields.each_with_index |f,i|
counts[i] += 1 if values[i] == f
end
else
# init search strings
values = fields
counts = Array.new(values.size, 0)
end
end

Cheers

robert


#3

Mmcolli00 Mom wrote:

#Searches through a text file and, at one row at a time, counts how many
occurrences of the value exist for the column. For instance, if word,
‘bird’ exists in column1 row1 then count how many birds exist for the
whole column. Next…count the occurrences of bird in the column 2. If
the numbers do not match then outputs “Value does not contain a match:”
+Value.

— Here’s a simple version

count1 = Hash.new(0) # word => count in col1
count2 = Hash.new(0) # word => count in col2

File.open(“temp.txt”) do |src|
src.each_line do |line|
word1, word2 = line.chomp.split(",")
count1[word1] += 1
count2[word2] += 1
end
end

words = (count1.keys + count2.keys).uniq
words.each do |word|
if count1[word] != count2[word]
puts “Value does not contain a match: #{word}”
end
end

— However you can remove some duplication by choice
— of a suitable data structure: a single hash which maps
— to an array giving the counts in col1 and col2

word => [col1_count, col2_count]

counts = Hash.new { |h,k| h[k] = Array.new(2,0) }

File.open(“temp.txt”) do |src|
src.each_line do |line|
line.chomp.split(",").each_with_index do |word, i|
counts[word][i] += 1
end
end
end
counts.each do |word, cols|
if cols.uniq.size != 1
puts “Word #{word.inspect} occurs different times: #{cols.inspect}”
end
end


#4

-------- Original-Nachricht --------

Datum: Thu, 15 Jan 2009 13:51:06 +0900
Von: Mmcolli00 Mom removed_email_address@domain.invalid
An: removed_email_address@domain.invalid
Betreff: undefined method `[]’ for nil:NilClass - how to prevent?

occurrences of the value exist for the column. For instance, if word,
col1 = 0
if array[0] == arrayVal1 then
puts arrayVal1
Posted via http://www.ruby-forum.com/.
Hi —
in some lines of your data, the split function doesn’t find the
appropriate pattern, so you have
array==[], and thus, there isn’t any array[0], i.e. array[0]==nil, and
for nil, which is NilClass rather
than Array, there are no elements defined.
An alternative would be to do something like this:

begin
arrayVal1 = array[0][i]
rescue
arrayVal1=[“something_else_you’ll_have_to_fill_in”]
end
begin
arrayVal2 = array[0][i]
rescue
arrayVal2=[“still_something_else_you’ll_have_to_fill_in”]
end

which will try to execute the first statement after “begin”, and if that
fails, the statement after “rescue”.
A conceptually cleaner way yet would be to assign a default value to the
array variable,

array=Array.new([‘nothing in here’])

Best regards,

Axel


#5

Brian Thanks!! Your code does exactly what I am needing it to do. Thanks
for taking out the duplicates also. You are very skillful!

This error occurred:
I’ll am looking to see if maybe I am missing a gem, not sure.

Value does not contain a match: Clashifting.rb:55: undefined method +' for >nil:NilClass (NoMethodError) from shifting.rb:143:ineach_with_index’
from shifting.rb:54:in each' from shifting.rb:54:ineach_with_index’
from shifting.rb:54
from shifting.rb:53:in each_line' from shifting.rb:53 from shifting.rb:52:inopen’
from shifting.rb:52

snippet of your code:

File.open(“temp.txt”) do |src|
src.each_line do |line|
line.chomp.split(",").each_with_index do |word, i|
counts[word][i] += 1 #line 55
end
end
end
counts.each do |word, cols|
if cols.uniq.size != 1
puts “Word #{word.inspect} occurs different times: #{cols.inspect}”
end
end


#6

Axel E. wrote:

A conceptually cleaner way yet would be to assign a default value to the
array variable,

array=Array.new([‘nothing in here’])

Note: unlike Hashes, Arrays don’t have default values.

When creating an Array, you can fill it with a specific number of
values:

array = Array.new(5,99) # [99,99,99,99,99]

This form inserts n identical references to the same object. If you
want distinct objects, then you have to use the block form:

array = Array.new(5) { [‘nothing in here’] }

Regards,

Brian.


#7

Mmcolli00 Mom wrote:

I’ll am looking to see if maybe I am missing a gem, not sure.

Value does not contain a match: Clashifting.rb:55: undefined method `+’ for >nil:NilClass (NoMethodError)

counts[word][i] += 1 #line 55

Well, this expression is a shortcut for

counts[word][i] = counts[word][i] + 1

so this means that counts[word][i] (on the right hand side) is nil. This
will occur if a line of your data file has more than 2 columns.

Note that

counts[‘someword’] is initialised to Array(2,0), which is [0,0]

so:

counts[‘someword’][0] is 0
counts[‘someword’][1] is 0
counts[‘someword’][2] is nil … same for [3], [4] etc.

So it’s reading past the end of the array, getting nil, and trying to
add 1 to it. The expression nil + 1 is meaningless, of course.

The solution depends on what you want to happen when your program reads
a line of the form “a,b,c”

  • Do you want to treat “a” as the first word, and “b,c” as the second?
  • Do you want to treat “a” and “b” as words, and ignore the rest of the
    line?

Suitable adjustment to the ‘split’ expression should do the trick.

irb(main):001:0> “a,b,c”.split(",",2)
=> [“a”, “b,c”]
irb(main):002:0> “a,b,c”.split(",")[0,2]
=> [“a”, “b”]

HTH,

Brian.