Forum: Ruby undefined method `[]' for nil:NilClass - how to prevent?

Cf63da956b6ba955687a2f2f262928cb?d=identicon&s=25 Mmcolli00 Mom (mmcolli00)
on 2009-01-15 05:52
Attachment: searchArray.rb (1 KB)
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:in `each_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
E0d864d9677f3c1482a20152b7cac0e2?d=identicon&s=25 Robert Klemme (Guest)
on 2009-01-15 08:06
(Received via mailing list)
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
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-15 10:31
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
D0338c0de4cb3c5c17300396159933d1?d=identicon&s=25 Axel Etzold (Guest)
on 2009-01-15 10:38
(Received via mailing list)
-------- Original-Nachricht --------
> Datum: Thu, 15 Jan 2009 13:51:06 +0900
> Von: Mmcolli00 Mom <mmc_collins@yahoo.com>
> An: ruby-talk@ruby-lang.org
> 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
Cf63da956b6ba955687a2f2f262928cb?d=identicon&s=25 Mmcolli00 Mom (mmcolli00)
on 2009-01-15 20:58
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:in `each_with_index'
>  from shifting.rb:54:in `each'
>  from shifting.rb:54:in `each_with_index'
>  from shifting.rb:54
>  from shifting.rb:53:in `each_line'
>  from shifting.rb:53
>  from shifting.rb:52:in `open'
>  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
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-15 21:59
Axel Etzold 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.
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2009-01-15 22:08
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.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.