lib/map.rb in map-2.7.1 vs lib/map.rb in map-2.8.0

- old
+ new

@@ -1,7 +1,7 @@ class Map < Hash - Version = '2.7.1' unless defined?(Version) + Version = '2.8.0' unless defined?(Version) Load = Kernel.method(:load) unless defined?(Load) class << Map def version Map::Version @@ -143,12 +143,21 @@ end end args end - alias_method '[]', 'new' + + def intersection(a, b) + a, b, i = Map.for(a), Map.for(b), Map.new + a.depth_first_each{|key, val| i.set(key, val) if b.has?(key)} + i + end + + def match(haystack, needle) + intersection(haystack, needle) == needle + end end unless defined?(Dynamic) Dynamic = lambda do conversion_methods.reverse_each do |method| @@ -441,26 +450,58 @@ val = delete(key) [key, val] end end -# misc +# equality / sorting / matching support # - def ==(hash) - return false unless(Map === hash) - return false if keys != hash.keys - super hash + def ==(other) + case other + when Map + return false if keys != other.keys + super(other) + + when Hash + self == Map.from_hash(other, self) + + else + false + end end def <=>(other) - keys <=> klass.coerce(other).keys + cmp = keys <=> klass.coerce(other).keys + return cmp unless cmp.zero? + values <=> klass.coerce(other).values end def =~(hash) to_hash == klass.coerce(hash).to_hash end +# reordering support +# + def reorder(order = {}) + order = Map.for(order) + map = Map.new + keys = order.depth_first_keys | depth_first_keys + keys.each{|key| map.set(key, get(key))} + map + end + + def reorder!(order = {}) + replace(reorder(order)) + end + +# support for building ordered hasshes from a map's own image +# + def Map.from_hash(hash, order = nil) + map = Map.for(hash) + map.reorder!(order) if order + map + end + def invert inverted = klass.allocate inverted.default = self.default keys.each{|key| inverted[self[key]] = key } inverted @@ -695,10 +736,12 @@ def alphanumeric_key_for(key) Map.alphanumeric_key_for(key) end +## TODO - technically this returns only leaves so the name isn't *quite* right. re-factor for 3.0 +# def Map.depth_first_each(enumerable, path = [], accum = [], &block) Map.pairs_for(enumerable) do |key, val| path.push(key) if((val.is_a?(Hash) or val.is_a?(Array)) and not val.empty?) Map.depth_first_each(val, path, accum) @@ -708,14 +751,26 @@ path.pop() end if block accum.each{|keys, val| block.call(keys, val)} else - [path, accum] + accum end end + def Map.depth_first_keys(enumerable, path = [], accum = [], &block) + accum = Map.depth_first_each(enumerable, path = [], accum = [], &block) + accum.map!{|kv| kv.first} + accum + end + + def Map.depth_first_values(enumerable, path = [], accum = [], &block) + accum = Map.depth_first_each(enumerable, path = [], accum = [], &block) + accum.map!{|kv| kv.last} + accum + end + def Map.pairs_for(enumerable, *args, &block) if block.nil? pairs, block = [], lambda{|*pair| pairs.push(pair)} else pairs = false @@ -734,12 +789,60 @@ end pairs ? pairs : result end + def Map.breadth_first_each(enumerable, accum = [], &block) + levels = [] + + keys = Map.depth_first_keys(enumerable) + + keys.each do |key| + key.size.times do |i| + k = key.slice(0, i + 1) + level = k.size - 1 + levels[level] ||= Array.new + last = levels[level].last + levels[level].push(k) unless last == k + end + end + + levels.each do |level| + level.each do |key| + val = enumerable.get(key) + block ? block.call(key, val) : accum.push([key, val]) + end + end + + block ? enumerable : accum + end + + def Map.keys_for(enumerable) + keys = enumerable.respond_to?(:keys) ? enumerable.keys : Array.new(enumerable.size){|i| i} + end + def depth_first_each(*args, &block) Map.depth_first_each(enumerable=self, *args, &block) end + + def depth_first_keys(*args, &block) + Map.depth_first_keys(enumerable=self, *args, &block) + end + + def depth_first_values(*args, &block) + Map.depth_first_values(enumerable=self, *args, &block) + end + + def breadth_first_each(*args, &block) + Map.breadth_first_each(enumerable=self, *args, &block) + end + + def contains(other) + other = other.is_a?(Hash) ? Map.coerce(other) : other + breadth_first_each{|key, value| return true if value == other} + return false + end + alias_method 'contains?', 'contains' end module Kernel private def Map(*args, &block)