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)