Monday, July 19, 2010

Ruby On Rails - Hashes and symbols

>> user = {}                          # {} is an empty hash
=> {}
>> user["first_name"] = "Michael"     # Key "first_name", value "Michael"
=> "Michael"
>> user["last_name"] = "Hartl"        # Key "last_name", value "Hartl"
=> "Hartl"
>> user["first_name"]                 # Element access is like arrays
=> "Michael"
>> user                               # A literal representation of the hash
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}
Hashes are indicated with curly braces containing key-value pairs; a pair of braces with no key-value pairs—i.e.,{}—is an empty hash. It’s important to note that the curly braces for hashes have nothing to do with the curly braces for blocks. (Yes, this can be confusing.) Though hashes resemble arrays, one important difference is that hashes don’t generally guarantee keeping their elements in a particular order.11 If order matters, use an array.
Instead of defining hashes one item at a time using square brackets, it’s easy to use their literal representation:
>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}
Here I’ve used the usual Ruby convention of putting an extra space at the two ends of the hash—a convention ignored by the console output. (Don’t ask me why the spaces are conventional; probably some early influential Ruby programmer liked the look of the extra spaces, and the convention stuck.)
So far we’ve used strings as hash keys, but in Rails it is much more common to use symbols instead. Symbols look kind of like strings, but prefixed with a colon instead of surrounded by quotes. For example, :name is a symbol. You can think of symbols as basically strings without all the extra baggage:12
>> "name".length
>> :name.length
NoMethodError: undefined method `length' for :name:Symbol
>> "foobar".reverse
=> "raboof"
>> :foobar.reverse
NoMethodError: undefined method `reverse' for :foobar:Symbol
Symbols are a special Ruby data type shared with very few other languages, so they may seem weird at first, but Rails uses them a lot, so you’ll get used to them fast.
In terms of symbols as hash keys, we can define a user hash as follows:
>> user = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]              # Access the value corresponding to :name.
=> "Michael Hartl"
>> user[:password]          # Access the value of an undefined key.
=> nil
We see here from the last example that the hash value for an undefined key is simply nil.
Hash values can be virtually anything, even other hashes, as seen in Listing 4.5.
Listing 4.5. Nested hashes.
>> params = {}        # Define a hash called 'params' (short for 'parameters').
=> {}
>> params[:user] = { :name => "Michael Hartl", :email => "mhartl@example.com" }
=> {:name=>"Michael Hartl", :email=>"mhartl@example.com"}
>> params
=> {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}}
>>  params[:user][:email]
=> "mhartl@example.com"
These sorts of hashes-of-hashes, or nested hashes, are heavily used by Rails, as we’ll see starting in Section 8.2.
As with arrays and ranges, hashes respond to the each method. For example, consider a hash named flash with keys for two conditions, :success and :error:
>> flash = { :success => "It worked!", :error => "It failed. :-(" }
=> {:success=>"It worked!", :error=>"It failed. :-("}
>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :error has value "It failed. :-("
Note that, while the each method for arrays takes a block with only one variable, each for hashes takes two, akey and a value. Thus, the each method for a hash iterates through the hash one key-value pair at a time.
The last example uses the useful inspect method, which returns a string with a literal representation of the object it’s called on:
>> puts flash            # Put the flash hash as a string (with ugly results).
successIt worked!errorIt failed. :-(
>> puts flash.inspect    # Put the flash hash as a pretty string
{:success=>"It worked!", :error=>"It failed. :-("}
>> puts :name, :name.inspect
>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"
By the way, using inspect to print an object is common enough that there’s a shortcut for it, the p function:
>> p flash               # Same as 'puts flash.inspect'
{:success=>"It worked!", :error=>"It failed. :-("}


Post a Comment

Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Macys Printable Coupons