You are on page 1of 6

4.3.

3 Hashesandsymbols
Hashesareessentiallyarraysthatarentlimitedtointegerindices. (Infact,somelanguages,especiallyPerl,sometimes
callhashesassociativearraysforthisreason.) Instead,hashindices,orkeys,canbealmostanyobject. Forexample,we
canusestringsaskeys:
>> 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"}

Hashesareindicatedwithcurlybracescontainingkeyvaluepairsapairofbraceswithnokeyvaluepairsi.e.,{}isan
emptyhash. Itsimportanttonotethatthecurlybracesforhasheshavenothingtodowiththecurlybracesfor
blocks. (Yes,thiscanbeconfusing.) Althoughhashesresemblearrays,oneimportantdifferenceisthathashesdont
generallyguaranteekeepingtheirelementsinaparticularorder.10 Ifordermatters,useanarray.
Insteadofdefininghashesoneitematatimeusingsquarebrackets,itseasytousealiteralrepresentationwithkeysand
valuesseparatedby=>,calledahashrocket:
>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"last_name"=>"Hartl", "first_name"=>"Michael"}

HereIveusedtheusualRubyconventionofputtinganextraspaceatthetwoendsofthehashaconventionignoredby
theconsoleoutput. (DontaskmewhythespacesareconventionalprobablysomeearlyinfluentialRubyprogrammer
likedthelookoftheextraspaces,andtheconventionstuck.)

Sofarweveusedstringsashashkeys,butinRailsitismuchmorecommontousesymbolsinstead. Symbolslookkind
oflikestrings,butprefixedwithacoloninsteadofsurroundedbyquotes. Forexample,:nameisasymbol. Youcanthink
ofsymbolsasbasicallystringswithoutalltheextrabaggage:11
>> "name".split('')
=> ["n", "a", "m", "e"]
>> :name.split('')
NoMethodError: undefined method `split' for :name:Symbol
>> "foobar".reverse
=> "raboof"
>> :foobar.reverse
NoMethodError: undefined method `reverse' for :foobar:Symbol

SymbolsareaspecialRubydatatypesharedwithveryfewotherlanguages,sotheymayseemweirdatfirst,butRails
usesthemalot,soyoullgetusedtothemfast. Unlikestrings,notallcharactersarevalid:
>> :foo-bar
NameError: undefined local variable or method `bar' for main:Object
>> :2foo
SyntaxError

Aslongasyoustartyoursymbolswithaletterandsticktonormalwordcharacters,youshouldbefine.
Intermsofsymbolsashashkeys,wecandefineauserhashasfollows:
>> 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

Weseeherefromthelastexamplethatthehashvalueforanundefinedkeyissimplynil.

Becauseitssocommonforhashestousesymbolsaskeys,asofversion1.9Rubysupportsanewsyntaxjustforthis
specialcase:
>> h1 = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h2 = { name: "Michael Hartl", email: "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> h1 == h2
=> true

Thesecondsyntaxreplacesthesymbol/hashrocketcombinationwiththenameofthekeyfollowedbyacolonanda
value:
{ name: "Michael Hartl", email: "michael@example.com" }

Thisconstructionmorecloselyfollowsthehashnotationinotherlanguages(suchasJavaScript)andenjoysgrowing
popularityintheRailscommunity. Becausebothhashsyntaxesarestillincommonuse,itsessentialtobeableto
recognizebothofthem. Unfortunately,thiscanbeconfusing,especiallysince:nameisvalidonitsown(asastandalone
symbol)butname:hasnomeaningbyitself. Thebottomlineisthat:name =>andname:areeffectivelythesameonly
insideliteralhashes,sothat
{ :name => "Michael Hartl" }

and
{ name: "Michael Hartl" }

areequivalent,butotherwiseyouneedtouse:name(withthecoloncomingfirst)todenoteasymbol
Hashvaluescanbevirtuallyanything,evenotherhashes,asseeninListing4.13.

Listing4.13: Nestedhashes.
>> 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"

Thesesortsofhashesofhashes,ornestedhashes,areheavilyusedbyRails,aswellseestartinginSection7.3.
Aswitharraysandranges,hashesrespondtotheeachmethod. Forexample,considerahashnamedflashwithkeysfor
twoconditions,:successand:danger:
>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", :danger=>"It failed."}
>> flash.each do |key, value|
?> puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :danger has value "It failed."

Notethat,whiletheeachmethodforarraystakesablockwithonlyonevariable,eachforhashestakestwo,akeyand
avalue. Thus,theeachmethodforahashiteratesthroughthehashonekeyvaluepairatatime.
Thelastexampleusestheusefulinspectmethod,whichreturnsastringwithaliteralrepresentationoftheobjectits
calledon:
>> puts (1..5).to_a
1
2
3
4

# Put an array as a string.

5
>> puts (1..5).to_a.inspect
# Put a literal array.
[1, 2, 3, 4, 5]
>> puts :name, :name.inspect
name
:name
>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"

Bytheway,usinginspecttoprintanobjectiscommonenoughthattheresashortcutforit,thepfunction:12
>> p :name
:name

# Same output as 'puts :name.inspect'

Exercises

1.Defineahashwiththekeysone,two,andthree,andthevaluesuno,dos,andtres. Iterateoverthe
hash,andforeachkey/valuepairprintout"#key in Spanish is #value".
2.Createthreehashescalledperson1,person2,andperson3,withfirstandlastnamesunderthe
keys:firstand:last. Thencreateaparamshashso
thatparams[:father]isperson1,params[:mother]isperson2,andparams[:child]isperson3. Verifythat,
forexample,params[:father][:first]hastherightvalue.
3.Defineahashwithsymbolkeyscorrespondingtoname,email,andapassworddigest,andvaluesequaltoyour
name,youremailaddress,andarandomstringof16lowercaseletters.

4.FindanonlineversionoftheRubyAPIandreadabouttheHashmethodmerge. Whatisthevalueofthefollowing
expression?
{ "a" => 100, "b" => 200 }.merge({ "b" => 300 })

You might also like