Quick Tip: The Many Ways to Create a Hash in Ruby
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!