pjw23 pjw23 - 1 year ago 133
Ruby Question

Ruby - Verify hash key exists

I'm having a problem working with a nested hash and hoping to get some assistance. The data I'm working with looks like the following:

=> {"searchresults"=>
{"request"=>{"address"=>"10 Tiverton Ln", "citystatezip"=>"28803"},
"message"=>{"text"=>"Request successfully processed", "code"=>"0"},
{"street"=>"10 Tiverton Ln",
{"amount"=>{"__content__"=>"444956", "currency"=>"USD"},
"valueChange"=>{"__content__"=>"752", "duration"=>"30", "currency"=>"USD"},
{"low"=>{"__content__"=>"413809", "currency"=>"USD"}, "high"=>{"__content__"=>"480552", "currency"=>"USD"}},
"http://www.zillow.com/static/xsd/SearchResults.xsd http://www.zillowstatic.com/vstatic/5fa4733/static/xsd/SearchResults.xsd"}}

I'm trying to grab specific information from the hash. The problem I'm having is: If the hash key isn't present, it throws an error and kills my script.

To re-mediate this issue, I figured I would just verify the key presence beforehand, however, my check doesn't appear to be working properly. I'm using the "key?" method, but I'm obviously doing something wrong since in my verification (when the key is there) results in a "false" back from Ruby.

=> "5620805"
=> false

(NOTE: I realized the last input was just a concatenated string based on comment, but can't get any combination to work, see below-towards bottom of post)

What is the best way to get around errors when querying for a hash key that doesn't exist? If the "key?" method is the proper way to go about it, can someone please point me in the right direction of where my error is.

Added info based on responses. I'm using the following version of ruby

ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]

I think my issues have something to do with the fact that I'm working with a nested hash, but I can't seem to get the dig method to work properly on the data. If anyone sees what I'm doing wrong please let me know as I believe this will fix my errors.

=> nil
=> false
hash.dig("searchresults", "response", "results", "result", "zpid")
=> "5620805"
hash.key?(hash.dig("searchresults", "response", "results", "result", "zpid"))
=> false

Since dig method isn't working, I'm using the full nested path, however, I can't seem to get the syntax right and get the following errors.

Calling the key, with the same method that I pull the value from (only change is ".key?"

ArgumentError: wrong number of arguments (given 0, expected 1)
from /home/pjw/.rvm/gems/ruby-2.3.0/gems/httparty-0.14.0/lib/httparty/response.rb:81:in `key?'

Using "()" seperated by ","

ArgumentError: wrong number of arguments (given 5, expected 1)
from /home/pjw/.rvm/gems/ruby-2.3.0/gems/httparty-0.14.0/lib/httparty/response.rb:81:in `key?'

Using "()" seperated by "."

SyntaxError: unexpected ')', expecting end-of-input

Can someone please help me with this it seems that whichever way I go with trying to solve doesn't appear to be working. Thanks in advance.

Answer Source
h = { "searchresults"=>
        { "request" =>{ "address"=>"10 Tiverton Ln", "citystatezip"=>"28803" },
          "message" =>{ "text"=>"Request successfully processed", "code"=>"0" },
          "response"=>{ "results"=>{
                                     "result"=>{ "zpid"=>"5620805" }

As @muistooshort pointed out in a comment, Hash#dig can be used here if you are using Ruby v2.3+.

  #=> {"request"=>{"address"=>"10 Tiverton Ln", "citystatezip"=>"28803"},
  #    "message"=>{"text"=>"Request successfully processed", "code"=>"0"},
  #    "response"=>{"results"=>{"result"=>{"zpid"=>"5620805"}}}} 
h.dig("searchresults", "response")
  #=> {"results"=>{"result"=>{"zpid"=>"5620805"}}}
h.dig("searchresults", "response", "results")
  #=> {"result"=>{"zpid"=>"5620805"}} 
h.dig("searchresults", "response", "results", "result")
  #=> {"zpid"=>"5620805"} 
h.dig("searchresults", "response", "results", "result", "zpid")
  #=> "5620805"

h.dig("searchresults", "cat", "results")
  #=> nil
h.dig("searchresults", "response", "results", "result", "dog")
  #=> nil 

For earlier versions of Ruby you can use Enumerable#reduce (aka inject).

def my_dig(h, *keys)
  keys.reduce(h) { |g,k| g && g[k] }

my_dig(h, "searchresults")
  #=> {"request"=>{"address"=>"10 Tiverton Ln", "citystatezip"=>"28803"},
  #    "message"=>{"text"=>"Request successfully processed", "code"=>"0"},
  #    "response"=>{"results"=>{"result"=>{"zpid"=>"5620805"}}}} 
my_dig(h, "searchresults", "response")
  #=> {"results"=>{"result"=>{"zpid"=>"5620805"}}}
my_dig(h, "searchresults", "response", "results")
  #=> {"result"=>{"zpid"=>"5620805"}} 
my_dig(h, "searchresults", "response", "results", "result")
  #=> {"zpid"=>"5620805"} 
my_dig(h, "searchresults", "response", "results", "result", "zpid")
  #=> "5620805" 

my_dig(h, "searchresults", "cat", "results")
  #=> nil 
my_dig(h, "searchresults", "response", "results", "result", "zpid", "dog")
  #=> nil 
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download