0

I have an an object Search with a method listings that returns an array of hashes if there are search results, otherwise it returns an empty array. In the event there is any empty array, I need to skip some code and go straight to the show page. I've tried object.nil? object.empty? object.present? all with the same outcome....the object which is supposed to be nil is treated as non-nil.

Controller code:

def show
    @search = Search.find(params[:id])
    @results = @search.listings
    if @results.last.present?
      if @results.last[0] == "p" || @results.last[0] == "s" || @results.last[0] == "d"
        p "@results.pop is either p, s, or d"
        @sort_column = @results.pop
        @grade = @sort_column.gsub(/[^0-9,.]/, "") unless @results.last[0] == "d"
      end
    end
  end

show action results in

undefined method `[]' for nil:NilClass
Extracted source (around line #21):

19        p "@results.pop is either p, s, or d"
20        @sort_column = @results.pop
21        @grade = @sort_column.gsub(/[^0-9,.]/, "") unless @results.last[0] == "d"
22      end
23    end
24  end

but, the server interface verifies that @results.last is nil:

>>  @results
=> []
>>  @results.last
=> nil
>>  @results.last.present?
=> false
>>  @results.last[0]
NoMethodError: undefined method `[]' for nil:NilClass
    from /Users/tomb/Projects/schoolsparrow/app/controllers/searches_controller.rb:21:in `show'
>> 

I'm at a loss as to how to logic is getting past the results.last.present? when results.last is nil.

tomb
  • 1,374
  • 1
  • 13
  • 23

3 Answers3

1

If you're testing to see whether or not your array has any values in it, try:

@results.any?

An empty array is not nil, which is why your checks are failing.

EDIT:

A concise explanation of nil v. empty v. blank in Ruby on Rails

Mark
  • 6,112
  • 4
  • 21
  • 46
  • Thanks for the comment. I tried results.any? but still failing. Note I'm calling .present? not on the array, but on array.last. – tomb Oct 24 '18 at 16:12
  • No worries - you say it's still failing, how so? If @results is an empty array then @results.any? will return false (indicating it's empty), and if it has results then it will return true – Mark Oct 24 '18 at 16:13
  • Its failing by somehow making it it to line 21 where results.last[0] flips an error because results.last is nil. How is it getting past results.last.present? – tomb Oct 24 '18 at 16:15
  • Looks like you need to either wrap up line 21 in a conditional (if @results.any?), or add a check before line 21 which will cancel the method (return unless @results.any?) – Mark Oct 24 '18 at 16:16
  • If you don't want any more code to execute if there are no more results then it's best to: `return unless @results.any?` – Mark Oct 24 '18 at 16:16
  • `last` must return `nil` based on https://api.rubyonrails.org/classes/ActiveRecord/Result.html#method-i-last. Is your `@search.listings` an `ActiveRecord` relationship ?, or is your `Search` class something else, like a custom class using Solr or some other search/indexing mechanism ?, that could be overwriting some stuff. – fanta Oct 24 '18 at 16:17
  • Thanks for the question @fanta, I went into Search to answer your question and discovered the problem. `@results` will return one result due to an indicator that I pass into the array, even if there are no listings, so initially `@results` is not `nil`, its only when I call `.pop` that the `@results` become `nil`. Instead of `.present?` I need to have a conditional `if @results.count > 1` – tomb Oct 24 '18 at 16:44
  • @tomb glad you found the solution. keep on rocking! – fanta Oct 24 '18 at 17:24
0

Why don't you check your condition on @results.present? and not @results.last.present?.

@results.last would throw a NoMethodError if @result is nil

Shreesha A
  • 23
  • 4
0

To check if an array has elements use .any? or its opposite twin .empty?:

irb(main):006:0> [].empty?
=> true
irb(main):007:0> [].any?
=> false

.present? and .presence work on arrays but they are really more idiomatically correct for hashes like the params.

def show
  @search = Search.find(params[:id])
  @results = @search.listings
  if @results.any? && ['p', 's', 'd'].include?(@results.last[0])
    p "@results.pop is either p, s, or d"
    @sort_column = @results.pop
    @grade = @sort_column.gsub(/[^0-9,.]/, "") unless @results.last[0] == "d"
  end
end
max
  • 96,212
  • 14
  • 104
  • 165