#-- # Dictionary # # Copyright (c) 2005 Thaoms Sawyer, Jan Molic # ported from OrderHash 2.0, Copyright (c) 2005 jan molic # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # ========================================================================== # Revision History: # -------------------------------------------------------------------------- # 2055.02.01 trans # * ported to mega from jan molic's OrderHash # # 2005.10.26 trans # * changed name of class to Dictionary # * fixed #update method # * added #dup and #insert # ========================================================================== #++ #:title: Dictionary # # Hash with preserved order and some array-like extensions. # # == Usage # # Just require this file and use Dictionary instead of Hash. # # # You can do simply # hsh = Dictionary.new # hsh['z'] = 1 # hsh['a'] = 2 # hsh['c'] = 3 # p hsh.keys #=> ['z','a','c'] # # # or using Dictionary[] method # hsh = Dictionary['z', 1, 'a', 2, 'c', 3] # p hsh.keys #=> ['z','a','c'] # # # but this don't preserve order # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3] # p hsh.keys #=> ['a','c','z'] # # # Dictionary has useful extensions: push, pop and unshift # p hsh.push('to_end', 15) #=> true, key added # p hsh.push('to_end', 30) #=> false, already - nothing happen # p hsh.unshift('to_begin', 50) #=> true, key added # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"] # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil # p hsh.keys #=> ["to_begin", "a", "c", "z"] # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil # # == Author(s) # # * jan molic # * trans # # == Special Thanks # # Andrew Johnson for his suggestions and fixes of Hash[], merge, # to_a, inspect and shift. # class Dictionary < Hash class << self def [] *args hsh = Dictionary.new if Hash === args[0] hsh.replace args[0] elsif (args.size % 2) != 0 raise ArgumentError, "odd number of elements for Hash" else hsh[args.shift] = args.shift while args.size > 0 end hsh end end attr_accessor :order def initialize @order = [] end def store_only a,b store a,b end alias_method :orig_store, :store protected :orig_store def insert(i,k,v) @order.insert(i,k) orig_store(k,v) end alias_method :unordered_store, :store private :unordered_store def store a,b @order.push a unless has_key? a super a,b end alias []= store def == hsh2 return false if @order != hsh2.order super hsh2 end def clear @order = [] super end def delete key @order.delete key super end def each_key @order.each { |k| yield k } self end def each_value @order.each { |k| yield self[k] } self end def each @order.each { |k| yield k,self[k] } self end alias each_pair each def delete_if @order.clone.each { |k| delete k if yield } self end def values ary = [] @order.each { |k| ary.push self[k] } ary end def keys @order end def invert hsh2 = Hash.new @order.each { |k| hsh2[self[k]] = k } hsh2 end def reject &block self.dup.delete_if &block end def reject! &block hsh2 = reject &block self == hsh2 ? nil : hsh2 end def replace hsh2 @order = hsh2.keys super hsh2 end def shift key = @order.first key ? [key,delete(key)] : super end def unshift k,v unless self.include? k @order.unshift k orig_store(k,v) true else false end end def push k,v unless self.include? k @order.push k orig_store(k,v) true else false end end def pop key = @order.last key ? [key,delete(key)] : nil end def to_a ary = [] each { |k,v| ary << [k,v] } ary end def to_s self.to_a.to_s end def inspect ary = [] each {|k,v| ary << k.inspect + "=>" + v.inspect} '{' + ary.join(", ") + '}' end def dup self.class[*to_a.flatten] end def update hsh2 hsh2.each { |k,v| self[k] = v } self end alias :merge! update def merge hsh2 self.dup.update(hsh2) end def select ary = [] each { |k,v| ary << [k,v] if yield k,v } ary end end # _____ _ # |_ _|__ ___| |_ # | |/ _ \/ __| __| # | | __/\__ \ |_ # |_|\___||___/\__| # =begin testing require 'test/unit' class TC_Dictionary < Test::Unit::TestCase def test_create hsh = Dictionary['z', 1, 'a', 2, 'c', 3] assert_equal( ['z','a','c'], hsh.keys ) end def test_op_store hsh = Dictionary.new hsh['z'] = 1 hsh['a'] = 2 hsh['c'] = 3 assert_equal( ['z','a','c'], hsh.keys ) end def test_push hsh = Dictionary['a', 1, 'c', 2, 'z', 3] assert( hsh.push('end', 15) ) assert_equal( 15, hsh['end'] ) assert( ! hsh.push('end', 30) ) assert( hsh.unshift('begin', 50) ) assert_equal( 50, hsh['begin'] ) assert( ! hsh.unshift('begin', 60) ) assert_equal( ["begin", "a", "c", "z", "end"], hsh.keys ) assert_equal( ["end", 15], hsh.pop ) assert_equal( ["begin", "a", "c", "z"], hsh.keys ) assert_equal( ["begin", 50], hsh.shift ) end def test_insert # front h = Dictionary['a', 1, 'b', 2, 'c', 3] r = Dictionary['d', 4, 'a', 1, 'b', 2, 'c', 3] assert_equal( 4, h.insert(0,'d',4) ) assert_equal( r, h ) # back h = Dictionary['a', 1, 'b', 2, 'c', 3] r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4] assert_equal( 4, h.insert(-1,'d',4) ) assert_equal( r, h ) end def test_update # with other orderred hash h1 = Dictionary['a', 1, 'b', 2, 'c', 3] h2 = Dictionary['d', 4] r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4] assert_equal( r, h1.update(h2) ) assert_equal( r, h1 ) # with other hash h1 = Dictionary['a', 1, 'b', 2, 'c', 3] h2 = { 'd' => 4 } r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4] assert_equal( r, h1.update(h2) ) assert_equal( r, h1 ) end def test_merge # with other orderred hash h1 = Dictionary['a', 1, 'b', 2, 'c', 3] h2 = Dictionary['d', 4] r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4] assert_equal( r, h1.merge(h2) ) # with other hash h1 = Dictionary['a', 1, 'b', 2, 'c', 3] h2 = { 'd' => 4 } r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4] assert_equal( r, h1.merge(h2) ) end end =end