From: John Carter Date: 2005-05-27T07:43:57+09:00 Subject: Re: hash of hash and complex data structure On Fri, 27 May 2005, John Carter wrote: > On Fri, 27 May 2005, ngoc wrote: > >> I am perl programmer, looking for new tools and interested in Ruby >> How in ruby similar to perl >> $a{$b}{$c}{$d} = 1; Oh yes, just as an aside, as a perl programmer I use to create quite a lot of these complex structures. As a ruby programmer I hardly ever do that. Not because ruby lacks the facility, but because I have found when I come back to the code three months later I haven't the faintest idea what the hell is going on. So these days I make use of the fact that it is really _very_ easy to spin a new class in Ruby or even extend Hash and I now make a bunch of small classes instead of hairy hash of hash structures. It is just so much easy to get my head around it. The only survivor of those perlish habits is my nifty ChainedHash class which I often use. (I see it was written before I discovered the block option on Hash.new) # A ChainedHash is a Hash that # stores every value in an array. # # However , "chained_hash[key]=value", a new value over an old one # doesn't replace the old one, it merely pushes it onto the end of the array. # class ChainedHash < Hash # Last in first out. def lifo(key) fetch(key)[-1] end # Last in first out. def fifo(key) fetch(key)[0] end # Push value onto the the end of the array associated with key. # if the hash doesn't have that key, stores [value] def []=(key,value) if has_key? key then fetch(key).push( value) else store(key, [value]) end end # Push value onto the the end of the array associated with key, # iff it isn't already there # if the hash doesn't have that key, stores [value] def store_unique(key, value) if has_key? key then array = fetch(key) array.push( value) unless array.include? value else store(key, [value]) end end def [](key) if has_key? key then fetch(key) else result = [] store(key, result) result end end # Iterates over each value array and each element in each array. def each_value(&block) super {|v| v.each(&block)} end # Iterates over each value array and each element in each array yielding key,value def each_key_value self.each_key do |key| self[key].each do |value| yield key,value end end end # Partition an enumerable based on whatever criteria is given in block # For example ChainedHash.multi_partition( %w{a b c aa d ddd ff}) {|e| e.length} # results in {1=>[a,b,c d], 2=>[aa, ff], 3=>[ddd]} # # For each element in any Enumerable enum, invoke block with that # element and accumulate the result in a chained hash. def ChainedHash.multi_partition(enum,&block) map = ChainedHash.new enum.each do |i| map[block.call(i)] = i end return map end end if $0 == __FILE__ then require 'test/unit' require 'yaml' class TC_ChainedHash < Test::Unit::TestCase def test_yaml h = ChainedHash.new h[:a] = 1 h[:a] = 2 h[:a] = 3 h[:a] = 4 h[:b] = 1 i = YAML::dump(h) j = YAML::load(i) assert_equal( h, j) end def test_each_value h = ChainedHash.new h[:a] = 1 h[:a] = 2 h[:a] = 3 h[:a] = 4 h[:b] = 1 c = [] h.each_value do |e| c << e end assert_equal( [1,1,2,3,4], c.sort) end def test_fetch h = ChainedHash.new assert_equal( [], h[3]) end def test_uniq h = ChainedHash.new h.store_unique( 1, 'a') h.store_unique( 2, 'a') h.store_unique( 2, 'a') h.store_unique( 1, 'a') h.store_unique( 1, 'b') assert_equal( 2, h.keys.length) assert_equal( 2, h[1].length) assert_equal( 1, h[2].length) assert_equal( 'a', h[2][0]) end def test_lifo h = ChainedHash.new h['a'] = 'x1' assert_equal( h.lifo( 'a'), 'x1') h['c'] = 'x2' assert_equal( h.lifo('c'), 'x2') h['a'] = 'x3' assert_equal( h.lifo('a'), 'x3') assert_equal( h.lifo('a'), 'x3') assert_equal( h['a'], ['x1','x3']) end def test_fifo h = ChainedHash.new h['a'] = 'x1' assert_equal( h.fifo('a'), 'x1') h['c'] = 'x2' assert_equal( h.fifo('c'), 'x2') h['a'] = 'x3' assert_equal( h.fifo('a'), 'x1') assert_equal( h.fifo('a'), 'x1') assert_equal( h['a'], ['x1', 'x3']) end def test_multi_part mp = ChainedHash.multi_partition( %w{a b c aa d ddd ff}) {|e| e.length} assert_equal( mp.keys.length, 3) assert_equal( mp[1], %w{a b c d}) end end end John Carter Phone : (64)(3) 358 6639 Tait Electronics Fax : (64)(3) 359 4632 PO Box 1645 Christchurch Email : john.carter@tait.co.nz New Zealand Carter's Clarification of Murphy's Law. "Things only ever go right so that they may go more spectacularly wrong later." From this principle, all of life and physics may be deduced.