Glenn,
A Hash can either have a default value:
h1 = Hash.new(:foo)
h1['new']
=> :foo
or a default proc
h2 = Hash.new { :foo }
h2['new']
=> :foo
but the proc doesn't create the entry in the hash
h2.keys
=>
See my first response or the rdoc for Hash.new
If you want to create the entry, you need to use the hash and key values passed to the block:
h3 = Hash.new {|h,k| h[k] = :foo }
h3['new']
=> :foo
h3.keys
=> ["new"]
h3
=> {"new"=>:foo}
You're trying to eat your cake and have it, too. An initialization similar to your recent post is needed.
def deep_hash(levels=1, default=nil)
if levels == 0
if default
Hash.new { |h,k| h[k] = default }
else
Hash.new
end
else
Hash.new { |h,k| h[k] = deep_hash(levels-1, default) }
end
end
if File.expand_path($0) == File.expand_path(__FILE__)
require 'test/unit'
class DeepHashTest < Test::Unit::TestCase
def test_zero_levels_and_no_default_is_normal_hash
expected = Hash.new
actual = deep_hash(0)
assert_equal expected, actual
assert_nil actual[:key]
assert_equal , actual.keys
end
def test_zero_levels_and_default_is_normal_hash
expected = Hash.new(42)
actual = deep_hash(0,42)
assert_equal expected, actual
assert_equal 42, actual[:key]
assert_equal [:key], actual.keys
end
def test_one_level_and_no_default_is_simple_nested_hash
expected = Hash.new {|h,k| h[k] = Hash.new}
actual = deep_hash(1)
assert_equal expected, actual
assert_nil actual[1][:key]
assert_equal [1], actual.keys
assert_equal , actual[1].keys
end
def test_one_level_and_default_is_simple_nested_hash
expected = Hash.new {|h,k| h[k] = Hash.new(43)}
actual = deep_hash(1,43)
assert_equal expected, actual
assert_equal 43, actual[1][:key]
assert_equal [1], actual.keys
assert_equal [:key], actual[1].keys
end
def test_three_levels_and_default_for_glenn
expected = Hash.new {|h3,k3| h3[k3] = Hash.new {|h2,k2| h2[k2] = Hash.new {|h,k| h[k] = Hash.new(44) } } }
actual = deep_hash(3, 44)
assert_equal expected, actual
assert(expected == actual, "Using ==")
assert_equal 44, actual[:a][:b][:c][:d]
actual[:a][:b][:c][:new] += 1
assert_equal 45, actual[:a][:b][:c][:new]
assert_equal [:d,:new], actual[:a][:b][:c].keys
end
end
end
__END__
The meaning of a certain number of levels is perhaps off-by-one relative to what you want and I'm not trying to allow the inner-most (deepest) hash to have a default_proc since it would need to have something like lambda {|h,k|h[k]=block.call}.
I suppose that can be left as an exercise for the reader. 
-Rob
Rob Biedenharn http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
···
On Jan 4, 2009, at 8:47 PM, Glenn wrote:
Hi David,
In your other post, there is no default value for the innermost hash.
hproc = lambda {|h,k| h[k] = Hash.new(&hproc) }
hash = Hash.new(&hproc)
So if I try to increment on a key that isn't there yet, like this:
hash[1][2][3] += 1
I get an undefined method for hash error.
Am I using it correctly if I want the inner hash to have a default? Please forgive me if I have missed something in your example. I am still relatively new to Ruby and am trying to get to the next level by writing more sophisticated programs.
Thanks,
Glenn
________________________________
From: David A. Black <dblack@rubypal.com>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
Sent: Sunday, January 4, 2009 4:22:37 PM
Subject: Re: Default values of hashes
Hi --
On Mon, 5 Jan 2009, Sebastian Hungerecker wrote:
Glenn wrote:
In other words, if I need 3 levels, it creates a hash within hash object 3
levels deep (like in the line of code above) and if I need 5, it creates
one 5 levels deep. I do not know how to do this (or even if it is
possible). Any suggestions on this one?
def nested_hash(levels, def_value=nil, &def_proc)
inner_hash = def_proc ? Hash.new(&def_proc) : Hash.new(def_value)
if levels == 1
inner_hash
else
l = [lambda {|h,k| h[k] = inner_hash}]
(levels-2).times do |i|
l[i+1] = lambda {|h,k| h[k] = Hash.new( &l[i] ) }
end
Hash.new(&l[-1])
end
This creates a nested hash levels levels deep, where the innermost hash can
optionally have a default value or proc.
Usage:
h = nested_hash(1)
# Same as h = Hash.new
h = nested_hash(1) {|h,k| h[k] = Array.new}
# Same as h = Hash.new {|h,k| h[k] = Array.new}
h = nested_hash(3)
# Same as h = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = Hash.new}}
h = nested_hash(3, :foo)
# Same as h = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = Hash.new(:foo)}}
Why not make it just keep going automatically? (See my other post.)
David
--
David A. Black / Ruby Power and Light, LLC
Ruby/Rails consulting & training: http://www.rubypal.com
Coming in 2009: The Well-Grounded Rubyist (http://manning.com/black2\)
http://www.wishsight.com => Independent, social wishlist management!