Search code examples
rubyarrayscsvsakai

Ruby Array#include? giving NoMethodError


I have a bit of code that checks if a user is in the "sakai_trained" array before proceeding. For some reason when I run this code:

CSV.foreach(activation_csv, {:headers => true}) do |row|
  if sakai_trained
    row << 'Untrained' unless sakai_trained.include?(row[1]) 
  end
  
  course_list << row
end

I get this error

C:/Ruby193/lib/ruby/1.9.1/csv.rb:478:in `==': undefined method `row' for "stuartademo":String (NoMethodError)
        from activate-courses.rb:42:in `include?'
        from activate-courses.rb:42:in `block in <main>'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1792:in `each'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1208:in `block in foreach'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1354:in `open'
        from C:/Ruby193/lib/ruby/1.9.1/csv.rb:1207:in `foreach'
        from activate-courses.rb:40:in `<main>'

I thought at first it was having a problem with the row[1] but it breaks the same way even with a string literal. I checked to make sure the sakai_trained array exists AND has data in it as well. I also tried rewriting it as an if statement in case the unless logic was flawed but that also returns the same error.

In case it's unclear, I want to check that the userid located in row[1] exists in the sakai_trained array before adding the row to the course_list array. If it doesn't, I want 'Untrained' added to the row first, then the row added to the array. When I removed the unless... part I was able to get a complete course_list array, but as expected, every row has "untrained". The problem appears to be with the

unless sakai_trained.include?(row[1])

part but I just can't see it.

Update:

sakai_trained = []
  CSV.foreach(training_csv, {:headers => true}) do |trained|
    sakai_trained << trained
  end

Should I #map! each item with .to_s to make them into strings then?

Update 2:

I changed

sakai_trained << trained

to

sakai_trained << trained.to_s

and it's removed the error, but the output still isn't quite right.

Update 3: ALMOST. WORKING. You guys are all incredibly awesome, and as frustrating as this is I have learned some new and interesting things.

Code:

course_list = []

if options[:verify]
  sakai_trained = []
  CSV.foreach(training_csv, {:headers => true}) do |trained|
    sakai_trained << trained.to_s
  end
end 
 
CSV.foreach(activation_csv, {:headers => true}) do |row|
  if sakai_trained && !sakai_trained.include?(row[1])  
    row << 'Untrained' 
  end
  
  course_list << row
end

Yields:

2124-5318,stuartademo,Untrained

2124-5320,bobsmith,Untrained

2124-4686,jimsmith,Untrained

2124-3560,jillsmith,Untrained

2124-3562,suesmith,Untrained

2124-5428,harrysmith,Untrained

When it should be

2124-5318,stuartademo,Untrained

2124-5320,bobsmith

2124-4686,jimsmith

2124-3560,jillsmith

2124-3562,suesmith

2124-5428,harrysmith


Solution

  • The problem is occurring inside the csv.rb file in the standard Ruby library on line 478. Here's the CSV code that is causing the problem for you:

    #
    # Returns +true+ if this row contains the same headers and fields in the
    # same order as +other+.
    #
    def ==(other)
      @row == other.row
    end
    

    From the looks of your error message, the String "stuartademo" is being passed into this method and, of course, there is no String#row. It looks like other should be a row of a csv file. According to the comments on the above method, it should contain headers and fields.

    I would suggest finding where this String "stuartademo" is coming from and figure out why only the String is getting passed in instead of the entire row.

    EDIT:

    If sakai_trained is populated from a CSV, then it is not an array but rather CSV:Row type. In this case, when you call CSV::Row#include? then the ==(other) is getting called. Hence, what you are passing into is, row[1] is a String. It should not be a String.

    Instead of using include?, try using field?(data) or fields.include?.

    CSV.foreach(activation_csv, {:headers => true}) do |row|
      if sakai_trained
        row << 'Untrained' unless sakai_trained.field?(row[1]) 
      end
    
      course_list << row
    end