Ruby
Article

Quick Tip: The Many Ways to Create a Hash in Ruby

By Glenn Goodrich

As Rubyists, we use Hashes for everything. They are just so danged easy and useful. I mean, how often do you need something keyed to something else? A lot, if you’re like me. For example, I have four (4) kids, which is a lot. I like to carry around a Hash like this one so I don’t forget their names:

$> kids = {1: "Logan", 2: "Emma", 3: "Lily", 4: "Becca"}

Well, this is how I started, but that is little more than a glorified array. Plus, I found myself needing all kinds of information about them, so I changed the hash:

$> kids = {logan: { gender: :boy, dob: Date.new(2000, 12,24), sports: [:soccer], favorite: false } ... }

You get the idea. This Hash has served me well for quite some time, but I thought I’d look into some different ways to create this hash to see if I could make it even more useful.

This is Your Grandparents’ Hash Creation

OK, let’s knock out the normal, boring ways to create a Hash. First, there’s what I did above, which is the “shorthand” syntax to create a Hash:

$> kids = {logan: { gender: :boy, dob: Date.new(2000, 12,24), sports: [:soccer], favorite: false } ... }

While it’s not sexy or exciting, it is the way 99.99999% of Hashes are created in Ruby. It kinda looks like JSON. In fact, I often wonder if Hashes and JSON objects all hang out together at some places called Value Bar (it’s in the Keys, get it?) and intermingle. But, I digress.

The other yawn-inducing method of creating a Hash is by calling the .new method:

$> kids = Hash.new
=> {}

That gives us an empty Hash. Were you expecting me to do this?

$> hash = Hash.new(logan: {...})

If you were then, WOW, are you in for a surprise! Hash.new isn’t as boring as you think.

Everything new is Fun Again

Hash.new adds some pizazz, nay flare, nay je ne sais quoi, to Hash creation. In the above example, we still get an empty Hash:

$> kids = Hash.new(logan: {...})
=> {}

But, now we get a new feature in the form of a default value. What this means is, if you ask the Hash to give you the value for a key that is not in the Hash, you’ll get the thing you passed to .new. Here’s how I improved my kids Hash with this feature:

$> kids = Hash.new({yours: false, send_them_home: true})
=> {}
$> kids[:colin]
=> {:yours=>false, :send_them_home=>true}

So, now, when Jill ({ wife: true, birthday: [elided], right: true}) says a kid’s name and that kid is not mine, I get some solid information and instruction on what to do. Sweet! By the way, you can see the default object of a hash by calling #default:

$> kids.default
=> {:yours=>false, :send_them_home=>true}

OR, you can override the default on a hash by calling hash.default = <the object>:

$> kids.default = {yours: false, embarrass: false }
=> {:yours=>false, :embarrass=>false}

But wait! There’s more! You can also pass a block to .new which will be run every time you ask for a key that is not in the Hash:

$> kids = Hash.new do |hash, key|
$>   puts "This is not your kid!"
$>   puts "Don't curse in front of them!"
$>   hash[key] = {yours: false, send_them_home: true}
$> end
=> {}
$> kids[:taylor]
This is not your kid!
Don't cuss in front of them!
 => {:yours=>false, :send_them_home=>true}

Excellent. Again, I have tapped into some of the lesser-known Hash initialization features to supply myself with great information. Notice, by the way, that when I pass a block I have to also store the key and value in the hash manually. If I don’t, the block will get run every time I access the same missing key. Also, just like you can override the default object for a Hash, you can override the default_proc:

$> kids.default_proc
  => #<Proc:0x007fc1eb2d7f80@(irb):15>
$> kids.default_proc = -> (hash, key) { puts "Find an adult" }

Keep Hash Creation Weird

For completeness sake, I’ll mention a couple of other ways to create a Hash. I am 100% sure these are completely useless and no one has ever used them in the history of Ruby programming.

Weird Hash-bracket method #1:

$> other_peoples_kids = Hash[ "colin", {friend_of: :logan, nerdy: true}, "taylor", {friend_of: :lily, annoying: true}]
 => {"colin"=>{:friend_of=>:logan, :nerdy=>true}, "taylor"=>{:friend_of=>:lily, :annoying=>true}}

So, you pass an array with an even number of items and that freak Hash splits them into key-value pairs.

Weird Hash-bracket method #2:

$> kids_interests = Hash[[[:logan, [:soccer, :pokemon]], [:emma, [:cheer, :not_mowing_the_lawn]]]]
 => {:logan=>[:soccer, :pokemon], :emma=>[:cheer, :not_mowing_the_lawn]}

Here, passing in an array of 2-element arrays is converted into a Hash. Seriously, who came up with this?

Weird Hash-bracket method #3:

$> kids_favorite_foods = Hash["login"=>"wings", "emma"=>"pizza", "lily"=>"airheads", "becca"=>"unknown"]
  => {"login"=>"wings", "emma"=>"pizza", "lily"=>"airheads", "becca"=>"unknown"}

Apparently, if I pass in a Hash to Hash[] I get a Hash. This is the Hash equivalent of eating corn.

These bracket methods are so odd that I picture the bad Hashes hanging out at the Value Bar “bracketing” to get high. Or something.

Friends Don’t Let Friends Bracket and Code

Well, that’s it for today’s Quick Tip. I hope you learned something about creating Hashes and/or my children. Speaking of which, the kids are all out so I am going to head to the pub for some bracketing. Good day!

More:
  • http://epigene.github.io/ Augusts Bautra

    `favorite: false` made my day!

    • ggsp

      Heh. That is the most often changing value.

  • http://ascentitservices.in/ Ascent IT Services

    Ascent IT Services company is providing low price for software development for web design, web development, for android, ios, windos in bihar, punjab, uttar pradesh all over India.

  • patshaughnessy

    Fun and informative post, thanks Glenn! I don’t need hashes to keep track of my kids, but I only have 2 :)

    Btw I use Weird Hash-bracket method #1 all the time to create a hash from an array, usually something like this:

    Hash[
    some_array.map {|element| some_code_here_that_returns_a_key_value_pair }
    ]

    • ggsp

      If ANYONE used those bracket methods, I **knew** it’d be you! I guess I’ll need to put those methods under a microscope so I can study them better…(get it?)

  • krim

    Hello. You have a typo in this example:
    >> puts “Don’t curse in front of them!”
    And then you have output:
    >> Don’t cuss in front of them!
    curse cuss

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Ruby, once a week, for free.