4

When trying to access an element deep in an array of arrays, what is the best way to avoid getting the error 'undefined method `[]' for nil:NilClass' if an element doesn't exist?

For example I'm currently doing this, but it seems bad to me:

if @foursquare['response']['groups'][0].present? && @foursquare['response']['groups'][0]['items'].present?
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Michael Irwin
  • 3,119
  • 5
  • 24
  • 40

3 Answers3

5

Ruby 2.3.0 introduced a new method called dig on both Hash and Array that can be combined with the new safe navigation operator (&.) to solve your problem.

@foursquare.dig('response', 'groups')&.first&.dig('items')

This will return nil if a value is missing at any level.

user513951
  • 12,445
  • 7
  • 65
  • 82
3

Depending on your array content, you can omit the .present?. Ruby will also just take the last value in such a construct, so you can omit the if statement.

@foursquare['response']['groups'][0] &&
@foursquare['response']['groups'][0]['items'] &&
@foursquare['response']['groups'][0]['items'][42]

More elegant solutions for this problem are the egonil (blog post), the andand gem (blog post), or even Ruby 2.3's safe navigation operator.

Update: Recent Rubies include the #dig method, which might be helpful in this case. See user513951's answer for more details.

J-_-L
  • 9,079
  • 2
  • 40
  • 37
  • 1
    I've wound up using this for now: `if @foursquare['response']['groups'].try(:[], 0).try(:[], 'items').present?`. Thanks! – Michael Irwin Jun 13 '11 at 19:16
2
if @foursquare['response']['groups'][0].to_a['items']
  . . .

It happens that NilClass implements a #to_a that returns []. This means that you can map every nil to [] and typically write a single expression without tests.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329