map icon indicating copy to clipboard operation
map copied to clipboard

the ruby container you've always wanted: an ordered string/symbol indifferent hash

NAME map.rb

SYNOPSIS the awesome ruby container you've always wanted: a string/symbol indifferent ordered hash that works in all rubies

maps are bitchin ordered hashes that are both ordered, string/symbol indifferent, and have all sorts of sweetness like recursive conversion, more robust implementation than HashWithIndifferentAccess, support for struct like (map.foo) access, and support for option/keyword access which avoids several nasty classes of errors in many ruby libraries

INSTALL gem install map

URI http://github.com/ahoward/map

DESCRIPTION

maps are always ordered. constructing them in an ordered fashion builds

them that way, although the normal hash contructor is also supported

m = Map[:k, :v, :key, :val] m = Map(:k, :v, :key, :val) m = Map.new(:k, :v, :key, :val)

m = Map[[:k, :v], [:key, :val]] m = Map(:k => :v, :key => :val) # ruh-oh, the input hash loses order! m = Map.new(:k => :v, :key => :val) # ruh-oh, the input hash loses order!

m = Map.new m[:a] = 0 m[:b] = 1 m[:c] = 2

p m.keys #=> ['a','b','c'] ### always ordered! p m.values #=> [0,1,2] ### always ordered!

maps don't care about symbol vs.string keys

p m[:a] #=> 0 p m["a"] #=> 0

even via deep nesting

p m[:foo]['bar'][:baz] #=> 42

many functions operate in a way one would expect from an ordered container

m.update(:k2 => :v2) m.update(:k2, :v2)

key_val_pair = m.shift key_val_pair = m.pop

maps keep mapiness for even deep operations

m.update :nested => {:hashes => {:are => :converted}}

maps can give back clever little struct objects

m = Map(:foo => {:bar => 42}) s = m.struct p s.foo.bar #=> 42

because option parsing is such a common use case for needing string/symbol

indifference map.rb comes out of the box loaded with option support

def foo(*args, &block) opts = Map.options(args) a = opts.getopt(:a) b = opts.getopt(:b, :default => false) end

opts = Map.options(:a => 42, :b => nil, :c => false) opts.getopt(:a) #=> 42 opts.getopt(:b) #=> nil opts.getopt(:b, :default => 42) #=> 42 opts.getopt(:c) #=> false opts.getopt(:d, :default => false) #=> false

this avoids such bugs as

options = {:read_only => false} read_only = options[:read_only] || true # should be false but is true

with options this becomes

options = Map.options(:read_only => true) read_only = options.getopt(:read_only, :default => false) #=> true

maps support some really nice operators that hashes/orderedhashes do not

m = Map.new m.set(:h, :a, 0, 42) m.has?(:h, :a) #=> true p m #=> {'h' => {'a' => [42]}} m.set(:h, :a, 1, 42.0) p m #=> {'h' => {'a' => [42, 42.0]}}

m.get(:h, :a, 1) #=> 42.0 m.get(:x, :y, :z) #=> nil m[:x][:y][:z] #=> raises exception!

m = Map.new(:array => [0,1]) defaults = {:array => [nil, nil, 2]} m.apply(defaults) p m[:array] #=> [0,1,2]

they also support some different iteration styles

m = Map.new

m.set( [:a, :b, :c, 0] => 0, [:a, :b, :c, 1] => 10, [:a, :b, :c, 2] => 20, [:a, :b, :c, 3] => 30 )

m.set(:x, :y, 42) m.set(:x, :z, 42.0)

m.depth_first_each do |key, val| p key => val end

#=> [:a, :b, :c, 0] => 0 #=> [:a, :b, :c, 1] => 10 #=> [:a, :b, :c, 2] => 20 #=> [:a, :b, :c, 3] => 30 #=> [:x, :y] => 42 #=> [:x, :z] => 42.0

USAGE see lib/map.rb and test/map_test.rb

HISTORY 4.3.0: - support for dot keys. map.set('a.b.c' => 42) #=> {'a'=>{'b'=>{'c'=>42}}}