Ruby Gem XKeys version 2.0.0 has just been released.
What Is It?
XKeys is a module that can be included in Ruby classes or used to extend Ruby objects to provide convenient handling of nested arrays or hashes, including Perl-like auto-vivification, PHP-like auto-indexing, and per-access default values.
Perl-Like Auto-Vivification For Ruby
A fairly common Ruby programming question, especially for current and former Perl programmers, is how to automatically generate intermediate nodes in nested array and hash structures.
Say, for example, that you want to keep some sort of running tally grouped by year, month, and day. In Perl, this is easily accomplished as follows:
my %tally; # top-level hash of tallies # and later... ++$tally{$year}{$month}{$day}; # increment tally by year/month/day
Perl will automatically create nested arrays or hashes as you attempt to write to them. They just “spring to life” when you need them; the process is called auto-vivification.
In straight Ruby, implementing the example is more cumbersome…
tally = {} # top-level hash of tallies # and later... tally[year] ||= {} # make sure year hash exists tally[year][month] ||= {} # make sure month hash exists tally[year][month][day] ||= 0 # make sure day value exists tally[year][month][day] += 1 # increment tally by year/month/day
Alternatively, you can provide a block of code to the top-level hash to create new hashes whenever a non-existent node is referenced, but they are created when reading (getting) the nested structure instead of when writing (setting) the nested structure, so you get new nodes even when you’re “just looking”.
Using the XKeys gem, the code becomes easier again:
require 'xkeys' tally = {}.extend XKeys::Hash # and later... tally[year, month, day, :else => 0] += 1
The “:else” value is used when the value doesn’t exist yet (this avoids generating an error trying to add 1 to nil on the first tally of each day). Missing nodes are automatically added, but only on write, not on read.
PHP-Like Auto-Indexing For Ruby
PHP allows you to auto-index items being added to the end of an array by leaving the array subscript empty. For example:
$languages = array(); $languages[] = 'Perl'; # assigned to $languages[0] $languages[] = 'PHP'; # assigned to $languages[1] $languages[] = 'Ruby'; assigned to $languages[2]
XKeys allows you to do something similar using the symbol :[] with arrays or other types of containers supporting the #push method. This is called “push mode”. In Ruby using XKeys, it looks like this:
require 'xkeys' languages = [].extend XKeys::Auto languages[:[]] = 'Perl' # languages.push 'Perl' ==> languages[0] languages[:[]] = 'PHP' # languages.push 'PHP' ==> languages[1] languages[:[]] = 'Ruby' # languages.push 'Ruby' ==> languages[2]