Daily Code Reading #2 – Facets Hash#zipnew

I’m continuing my look at Ruby Facets today, this time looking at another Hash method. I use Hashes everyday in Rails programming so if I can pick up some new techniques, I can put them to use right away. Today’s subject is Hash#zipnew.

The Code

1
2
3
4
5
6
# File lib/core/facets/hash/zipnew.rb, line 11
  def self.zipnew(keys,values) # or some better name
    h = {}
    keys.size.times{ |i| h[ keys[i] ] = values[i] }
    h
  end

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/ruby -wKU
#
# Code Reading #2
require '../base'
require "facets/hash/zipnew"
 
class HashZipnew
  def data
    keys = [
            '2010-05-15',
            '2010-05-16',
            '2010-05-17',
            '2010-05-18',
            '2010-05-19'
            ]
    values = [
              100.0,
              450.5,
              89.0,
              0.0,
              78.5
             ]
    Hash.zipnew(keys, values)
  end
end
 
ap(HashZipnew.new.data)
 
class HashZipnewTest < Test::Unit::TestCase
  def test_use_the_arrays_to_map_keys_to_values
    hashish = HashZipnew.new.data
 
    assert_equal 100.0, hashish['2010-05-15']
    assert_equal 450.5, hashish['2010-05-16']
    assert_equal  89.0, hashish['2010-05-17']
    assert_equal   0.0, hashish['2010-05-18']
    assert_equal  78.5, hashish['2010-05-19']
  end
 
end

On github

Review

Hash#zipnew is a awesome method. I can’t remember how many times I’ve collected two sets of time based data and had to merge them together, like when graphing time data. Hash#zipnew is pretty simple to use, just feed it two arrays of data; one for the keys and one for the values.

Internally, Hash#zipnew takes the size of the keys array and adds a new key/value to a hash. Running the loop by hand in irb shows this for the first two elements:

1
2
3
4
5
6
7
>> h = {}
{}
>> h['2010-05-15'] = 100.0
100.0
>> h['2010-05-16'] = 450.5
450.5
>> ...

I really like how #zipnew makes use of Array#size and Fixnum#times to automatically control it’s loops.

Since code reading should be an active activity, I’ve also created an alternative implementation that uses Array#with_index which would remove the #size and #times method calls.

1
2
3
4
5
  def self.alt_zipnew(keys, values)
    h = {}
    keys.each_with_index {|key, index| h[ key ] = values[ index ] }
    h
  end

This is the reason I’m interested in rubinius. Since they are using Ruby for (almost) the entire virtual machine, anyone can dig into the Ruby source code and play around with the internal implementation.