Hashes

Although arrays provide a good way of indexing a collection of items by number, sometimes it would be more convenient to index them in some other way. If, for example, you were creating a collection of recipes, it would be more meaningful to have each recipe indexed by name, such as “Rich Chocolate Cake” and “Coq au Vin,” rather than by numbers.

Ruby has a class that lets you do just that, called a hash. This is the equivalent of what some other languages call a dictionary or associative array. Just like a real dictionary, each entry is indexed by a unique key (in a real-life dictionary, this would be a word) that is associated with a value (in a dictionary, this would be the definition of the word).

Creating Hashes

Just like an array, you can create a hash by creating a new instance of the Hash class:

hash1.rb

h1 = Hash.new
h2 = Hash.new("Some kind of ring")

Both the previous examples create an empty Hash object. A Hash object always has a default value—that is, a value that is returned when no specific value is found at a given index. In these examples, h2 is initialized with the default value "Some kind of ring"; h1 is not initialized with a value, so its default value will be nil.

Having created a Hash object, you can add items to it using an arraylike syntax—that is, by placing the index in square brackets and using = to assign a value. The obvious difference is that, with an array, the index (or key) must be an integer; with a hash, it can be any unique data item:

h2['treasure1'] = 'Silver ring'
h2['treasure2'] = 'Gold ring'
h2['treasure3'] = 'Ruby ring'
h2['treasure4'] = 'Sapphire ring'

Often, the key may be a number or, as in the previous code, a string. In principle, however, a key can be any type of object. For example, given some class X, the following assignment is perfectly legal:

x1 = X.new('my Xobject')
h2[x1] = 'Diamond ring'

There is a shorthand way of creating Hashes and initializing them with key-value pairs. Just add a key followed by => and its associated value; each key-value pair should be separated by a comma and the whole lot placed inside a pair of curly brackets:

h1 = {    'room1'=>'The Treasure Room',
          'room2'=>'The Throne Room',
          'loc1'=>'A Forest Glade',
          'loc2'=>'A Mountain Stream' }

Indexing into a Hash

To access a value, place its key between square brackets:

puts(h1['room2'])            #=> 'The Throne Room'

If you specify a key that does not exist, the default value is returned. Recall that you have not specified a default value for h1, but you have for h2:

p(h1['unknown_room'])        #=> nil
p(h2['unknown_treasure'])    #=> 'Some kind of ring'

Use the default method to get the default value and the default= method to set it (see Chapter 2 for more information on get and set accessor methods):

p(h1.default)
h1.default = 'A mysterious place'

Copying a Hash

As with an array, you can assign one Hash variable to another, in which case both variables will refer to the same hash, and a change made using either variable will affect that hash:

hash2.rb

h4 = h1
h4['room1']='A new Room'
puts(h1['room1'])        #=> 'A new Room'

If you want the two variables to refer to the same items in different Hash objects, use the clone method to make a new copy:

h5 = h1.clone
h5['room1'] = 'An even newer Room'
puts(h1['room1'])        #=> 'A new room' (i.e., its value is unchanged)

Hash Order

The ordering of elements in a hash varies according to which version of Ruby you are using. In Ruby 1.8, a hash is generally stored in the order defined by its key where, for example, key 1 is less than key 2. When new items are added, these are inserted in key order. In Ruby 1.9, the hash is stored in the order in which it is defined. When new items are added, these are appended to the end of the hash.

As a general principle, it is best to make no assumptions about the order of elements in a hash. Most programming languages treat hashes or dictionaries as unordered collections. If you make the assumption that hash order is unpredictable, not only will you avoid bugs that may occur when running programs with different Ruby implementations, but you will also avoid problems that may arise when keys are of different types. Remember, a single hash may contain a mix of integer, string, and floating-point keys whose relative orders may not be self-evident.

hash_order.rb

h = {2=>"two", 1=>"one", 4=>"four" }
p( h )
h[3] = "three"
p( h )
h2 = {"one"=>1, 2=>"two", 4.5=>"four" }
p (h2)

When this code is run, Ruby 1.8 produces this output:

{1=>"one", 2=>"two", 4=>"four"}
{1=>"one", 2=>"two", 3=>"three", 4=>"four"}
{4.5=>"four", 2=>"two", "one"=>1}

But Ruby 1.9 shows this:

{2=>"two", 1=>"one", 4=>"four"}
{2=>"two", 1=>"one", 4=>"four", 3=>"three"}
{2=>"two", "one"=>1, 4.5=>"four"}

Sorting a Hash

If you want to ensure that the elements of a hash are in a specific order, you may sort them. As with the Array class, you may find a slight problem with the sort method of Hash. It expects to be dealing with keys of the same data type, so if, for example, you merge two arrays, one of which uses integer keys and another of which uses strings, you won’t be able to sort the merged hash. The solution to this problem is, as with Array, to write some code to perform a custom type of comparison and pass this to the sort method. You might give it a method, like this:

hash_sort.rb

def sorted_hash( aHash )
  return aHash.sort{
    |a,b|
      a.to_s <=> b.to_s
  }
end

This performs the sort based on the string representation (to_s) of each key in the hash. In fact, the Hash sort method converts the hash to a nested array of [key, value] arrays and sorts them using the Array sort method.

Hash Methods

The Hash class has numerous built-in methods. For example, to delete an item from a hash using its key, use the delete method:

aHash.delete( someKey )To test if a key or value exists, use the
 has_key? and has_value? methods:aHash.has_key?( someKey )
aHash.has_value?( someValue )

To combine two hashes, use the merge method: hash1.merge( hash2 ).

To return a new hash created using the original hash’s values as keys and its keys as values, use aHash.invert. To return an array populated with the hash’s keys or values, use aHash.keys and aHash.values.

Here’s an example that uses some of these methods:

hash_methods.rb

h1 = {
    'room1'=>'The Treasure Room',
    'room2'=>'The Throne Room',
    'loc1'=>'A Forest Glade',
    'loc2'=>'A Mountain Stream'
    }

h2 = {1=>'one', 2=>'two', 3=> 'three'}

h1['room1'] = 'You have wandered into a dark room'
h1.delete('loc2')
p(h1)
                         #=> {"room1"=>"You have wandered into a dark room",
                         #=> "room2"=>"The Throne Room",
                         #=> "loc1"=>"A Forest Glade"}
p(h1.has_key?('loc2'))   #=> false
p(h2.has_value?("two"))  #=>true
p(h2.invert)             #=> {"one"=>1, "two"=>2, "three"=>3}
p(h2.keys)               #=>[1, 2, 3]
p(h2.values)             #=>["one", "two", "three"]

If you want to find the position of an item in a hash, use the index method with Ruby 1.8 and the key method in Ruby 1.9. The index method is still present in Ruby 1.9 but is deprecated, so it may be removed in future versions:

h2.index("two")    # use this with Ruby 1.8
h2.key("two")      # use this Ruby 1.9
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.218.221