3

How do i reduce the following code to one line in ruby?

unless(data["location"].nil?)
  unless(data["location"]["country"].nil?)
    unless(data["location"]["country"]["code"].nil?)
      #some codes
    end
  end  
end
Sreeraj
  • 2,690
  • 6
  • 26
  • 37
  • This may be of interest: http://stackoverflow.com/questions/6672007/how-do-you-access-nested-elements-of-a-hash-with-a-single-string-key/6672263#6672263 (my answer shows how it can be a more general approach that handles early end-cases). –  Jul 13 '11 at 07:17
  • And for your next challenge, try reducing the characters instead of the lines :) I got to: "data.do_what_I_want" – jaydel Jul 13 '11 at 11:30

7 Answers7

10

if data["location"] && data["location"]["country"] && data["location"]["country"]["code]

Ruby's && operator is a short circut operator, so if the first operand is false, it will stop processing the rest of the condition. In addition. Any object that is not nil, is (for boolean purposes) true, so if the key exists, then it is true

Jim Deville
  • 10,632
  • 1
  • 37
  • 47
  • 1
    The point being that the `&&` operator "short-circuits", so the second part is only evaluated if the first part is true, and so on. +1 – Mike Woodhouse Jul 13 '11 at 07:23
  • @james..will `and` operator also work in the same manner? apart from having higher precedence, does `&&` have any other advantage over `and` – rubyprince Jul 13 '11 at 13:19
  • precendence is the only difference that i can think of. `and` short circuts as well, and to be honest, if you aren't worried about precedence, `and` is more readable – Jim Deville Jul 13 '11 at 16:19
3

You can use try method, this method supported from Rails 2.3 and has a native support from Ruby 1.9.

if data.try(:[],'location').try(:[],'country').try(:[],'code')
 ...
end
BitOfUniverse
  • 5,903
  • 1
  • 34
  • 38
1

There is always the good old ...

 if (data["location"]
     and data["location"]["country"] 
     and data["location"]["country"]["code"]) 
     # some code
 end
phtrivier
  • 13,047
  • 6
  • 48
  • 79
  • Well just remove the line breaks them ;) ... Now I understand the problem is how do this with less code. Unfortunately there is no built-in 'elvis' operator in Ruby (as in Groovy, If I'm not mistaken, where you can do seomthing like data?.location?.country?.code ) – phtrivier Jul 13 '11 at 09:15
  • I was just kidding. It's basically the same answer as James Deville's, which is probably the best solution. – Mischa Jul 13 '11 at 09:42
1

You can bring out the big guns and use the andand gem. It's not a production suggestion (or even terribly serious for real life), but is fascinating and directly addresses your need. With andand you can do

data['location'].andand['country'].andand['code']

You'll get a bonus warning about undefining object_id and how it may cause serious problems, but just smile and enjoy the metaprogramming :)

Peter
  • 127,331
  • 53
  • 180
  • 211
1

I know this is evil, but it's late for me….

class NilClass
  def [](args=""); self; end
end

if data["location"]["country"]["code"]
  # robot armies eat your pancakes
end
dogenpunk
  • 4,332
  • 1
  • 21
  • 29
0

An answer

!data["location"].nil? && !data["location"]["country"].nil? && !data["location"]["country"]["code"].nil? && #some codes

But why?

Ray Baxter
  • 3,181
  • 23
  • 27
0

Add a method to your Hash class

class Hash

  def all_keys

     keys = []

     each_pair do |k1, v1|

     if v1.is_a?(Hash)

        v1.each_pair { |k2, v2|
           if !v2.is_a?(Hash) then keys << [k1, k2]; next end
        v2.each_pair { |k3, v3|
           if !v3.is_a?(Hash) then keys << [k1, k2, k3]; next end
        v3.each_pair { |k4, v4|
           if !v4.is_a?(Hash) then keys << [k1, k2, k3, k4]; next end
        v4.each_pair { |k5, v5|
           if !v5.is_a?(Hash) then keys << [k1, k2, k3, k4, k5]; next end
        v5.each_pair { |k6, v6|
           if !v6.is_a?(Hash) then keys << [k1, k2, k3, k4, k5, k6]; next end
           # add more v[n].each_pair ... loops to process more hash dimensions
        } } } } }      # "}" * 5

        else
           keys << [k1]
        end

     end

    keys

 end

end

Now, use this code to check if the nested key exists:

data.all_keys.first.include? "code"