From: Rob Biedenharn Date: 2009-01-05T13:48:17+09:00 Subject: Re: Default values of hashes 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 > To: ruby-talk ML > 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 >> 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!