Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing hashes

Tags:

ruby

hash

I frequently write something like this:

a_hash['x'] ? a_hash['x'] += ' some more text' : a_hash['x'] = 'first text'

There ought to be a better way to do this, but I can't find it.

like image 465
Paul Avatar asked Jun 07 '10 15:06

Paul


1 Answers

There are two ways to create initial values with for a Hash.

One is to pass a single object in to Hash.new. This works well in many situations, especially if the object is a frozen value, but if the object has internal state, this may have unexpected side-effects. Since the same object is shared between all keys without an assigned value, modifying the internal state for one will show up in all.

a_hash = Hash.new "initial value"
a_hash['a'] #=> "initial value"
# op= methods don't modify internal state (usually), since they assign a new
# value for the key.
a_hash['b'] += ' owned by b' #=> "initial value owned by b"
# other methods, like #<< and #gsub modify the state of the string
a_hash['c'].gsub!(/initial/, "c's")
a_hash['d'] << " modified by d"
a_hash['e'] #=> "c's value modified by d"

Another initialization method is to pass Hash.new a block, which is invoked each time a value is requested for a key that has no value. This allows you to use a distinct value for each key.

another_hash = Hash.new { "new initial value" }
another_hash['a'] #=> "new initial value" 
# op= methods still work as expected
another_hash['b'] += ' owned by b'
# however, if you don't assign the modified value, it's lost,
# since the hash rechecks the block every time an unassigned key's value is asked for
another_hash['c'] << " owned by c" #=> "new initial value owned by c"
another_hash['c'] #=> "new initial value"

The block is passed two arguments: the hash being asked for a value, and the key used. This gives you the option of assigning a value for that key, so that the same object will be presented each time a particular key is given.

yet_another_hash = Hash.new { |hash, key| hash[key] = "#{key}'s initial value" }
yet_another_hash['a'] #=> "a's initial value"
yet_another_hash['b'] #=> "b's initial value"
yet_another_hash['c'].gsub!('initial', 'awesome')
yet_another_hash['c'] #=> "c's awesome value"
yet_another_hash #=> { "a" => "a's initial value", "b" => "b's initial value", "c" => "c's awesome value" }

This last method is the one I most often use. It's also useful for caching the result of an expensive calculation.

like image 156
rampion Avatar answered Sep 28 '22 08:09

rampion