timpone
timpone

Reputation: 19929

Ruby testing for definition of a variable

I know there are other questions similar such as:

  1. Ruby: how to check if variable exists within a hash definition
  2. Checking if a variable is defined?

But the answers aren't fully satisfactory.

I have:

ruby-1.9.2-p290 :001 > a=Hash.new
 => {} 
ruby-1.9.2-p290 :002 > a['one']="hello"
 => "hello"
ruby-1.9.2-p290 :006 > defined?(a['one']['some']).nil?
 => false 
ruby-1.9.2-p290 :007 > a['one']['some'].nil?
 => true

It seems like:

if a['one']['some'].nil?
  a['one']['some']=Array.new
end 

would be sufficient. Is this correct? Would this be correct for any data type? Is defined? needed in this case?

thx

Upvotes: 1

Views: 212

Answers (3)

tadman
tadman

Reputation: 211540

You seem to be confusing two concepts. One is if a variable is defined, and another is if a Hash key is defined. Since a hash is, at some point, a variable, then it must be defined.

defined?(a)
# => nil

a = { }
# => {}

defined?(a)
# => "local-variable"

a.key?('one')
# => false

a['one'] = 'hello'
# => 'hello'

a.key?('one')
# => true

Something can be a key and nil at the same time, this is valid. There is no concept of defined or undefined for a Hash. It is all about if the key exists or not.

The only reason to test with .nil? is to distinguish between the two possible non-true values: nil and false. If you will never be using false in that context, then calling .nil? is unnecessarily verbose. In other words, if (x.nil?) is equivalent to if (x) provided x will never be literally false.

What you probably want to employ is the ||= pattern that will assign something if the existing value is nil or false:

# Assign an array to this Hash key if nothing is stored there
a['one']['hello'] ||= [ ]

Update: Edited according to remarks by Bruce.

Upvotes: 2

Bruce
Bruce

Reputation: 8420

I had to dig a number of pages deep into Google, but I eventually found this useful bit from the Ruby 1.9 spec:

"In all cases the test [defined?] is conducted without evaluating the operand."

So what's happening is that it looks at:

a['one']['some']

and says "that is sending the "operator []" message to the 'a' object - that is a method call!" and the result of defined? on that is "method".

Then when you check against nil?, the string "method" clearly isn't nil.

Upvotes: 2

murphyslaw
murphyslaw

Reputation: 636

In addition to @tadmans answer, what you actually did in your example was to check, if the string "some" is included in the string "hello" which is stored in your hash at the position "one".

a = {}
a['one'] = 'hello'
a['one']['some'] # searches the string "some" in the hash at key "one"

A more simple example:

b = 'hello'
b['he'] # => 'he'
b['ha'] # => nil

That's why the defined? method did not return nil, as you expected, but "method".

Upvotes: 1

Related Questions