map 2.7.1 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/TODO +3 -0
- data/lib/map.rb +112 -9
- data/map.gemspec +1 -1
- data/test/map_test.rb +59 -0
- metadata +5 -5
data/TODO
CHANGED
data/lib/map.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Map < Hash
|
2
|
-
Version = '2.
|
2
|
+
Version = '2.8.0' unless defined?(Version)
|
3
3
|
Load = Kernel.method(:load) unless defined?(Load)
|
4
4
|
|
5
5
|
class << Map
|
@@ -145,8 +145,17 @@ class Map < Hash
|
|
145
145
|
|
146
146
|
args
|
147
147
|
end
|
148
|
-
|
149
148
|
alias_method '[]', 'new'
|
149
|
+
|
150
|
+
def intersection(a, b)
|
151
|
+
a, b, i = Map.for(a), Map.for(b), Map.new
|
152
|
+
a.depth_first_each{|key, val| i.set(key, val) if b.has?(key)}
|
153
|
+
i
|
154
|
+
end
|
155
|
+
|
156
|
+
def match(haystack, needle)
|
157
|
+
intersection(haystack, needle) == needle
|
158
|
+
end
|
150
159
|
end
|
151
160
|
|
152
161
|
unless defined?(Dynamic)
|
@@ -443,22 +452,54 @@ class Map < Hash
|
|
443
452
|
end
|
444
453
|
end
|
445
454
|
|
446
|
-
#
|
455
|
+
# equality / sorting / matching support
|
447
456
|
#
|
448
|
-
def ==(
|
449
|
-
|
450
|
-
|
451
|
-
|
457
|
+
def ==(other)
|
458
|
+
case other
|
459
|
+
when Map
|
460
|
+
return false if keys != other.keys
|
461
|
+
super(other)
|
462
|
+
|
463
|
+
when Hash
|
464
|
+
self == Map.from_hash(other, self)
|
465
|
+
|
466
|
+
else
|
467
|
+
false
|
468
|
+
end
|
452
469
|
end
|
453
470
|
|
454
471
|
def <=>(other)
|
455
|
-
keys <=> klass.coerce(other).keys
|
472
|
+
cmp = keys <=> klass.coerce(other).keys
|
473
|
+
return cmp unless cmp.zero?
|
474
|
+
values <=> klass.coerce(other).values
|
456
475
|
end
|
457
476
|
|
458
477
|
def =~(hash)
|
459
478
|
to_hash == klass.coerce(hash).to_hash
|
460
479
|
end
|
461
480
|
|
481
|
+
# reordering support
|
482
|
+
#
|
483
|
+
def reorder(order = {})
|
484
|
+
order = Map.for(order)
|
485
|
+
map = Map.new
|
486
|
+
keys = order.depth_first_keys | depth_first_keys
|
487
|
+
keys.each{|key| map.set(key, get(key))}
|
488
|
+
map
|
489
|
+
end
|
490
|
+
|
491
|
+
def reorder!(order = {})
|
492
|
+
replace(reorder(order))
|
493
|
+
end
|
494
|
+
|
495
|
+
# support for building ordered hasshes from a map's own image
|
496
|
+
#
|
497
|
+
def Map.from_hash(hash, order = nil)
|
498
|
+
map = Map.for(hash)
|
499
|
+
map.reorder!(order) if order
|
500
|
+
map
|
501
|
+
end
|
502
|
+
|
462
503
|
def invert
|
463
504
|
inverted = klass.allocate
|
464
505
|
inverted.default = self.default
|
@@ -697,6 +738,8 @@ class Map < Hash
|
|
697
738
|
Map.alphanumeric_key_for(key)
|
698
739
|
end
|
699
740
|
|
741
|
+
## TODO - technically this returns only leaves so the name isn't *quite* right. re-factor for 3.0
|
742
|
+
#
|
700
743
|
def Map.depth_first_each(enumerable, path = [], accum = [], &block)
|
701
744
|
Map.pairs_for(enumerable) do |key, val|
|
702
745
|
path.push(key)
|
@@ -710,10 +753,22 @@ class Map < Hash
|
|
710
753
|
if block
|
711
754
|
accum.each{|keys, val| block.call(keys, val)}
|
712
755
|
else
|
713
|
-
|
756
|
+
accum
|
714
757
|
end
|
715
758
|
end
|
716
759
|
|
760
|
+
def Map.depth_first_keys(enumerable, path = [], accum = [], &block)
|
761
|
+
accum = Map.depth_first_each(enumerable, path = [], accum = [], &block)
|
762
|
+
accum.map!{|kv| kv.first}
|
763
|
+
accum
|
764
|
+
end
|
765
|
+
|
766
|
+
def Map.depth_first_values(enumerable, path = [], accum = [], &block)
|
767
|
+
accum = Map.depth_first_each(enumerable, path = [], accum = [], &block)
|
768
|
+
accum.map!{|kv| kv.last}
|
769
|
+
accum
|
770
|
+
end
|
771
|
+
|
717
772
|
def Map.pairs_for(enumerable, *args, &block)
|
718
773
|
if block.nil?
|
719
774
|
pairs, block = [], lambda{|*pair| pairs.push(pair)}
|
@@ -736,9 +791,57 @@ class Map < Hash
|
|
736
791
|
pairs ? pairs : result
|
737
792
|
end
|
738
793
|
|
794
|
+
def Map.breadth_first_each(enumerable, accum = [], &block)
|
795
|
+
levels = []
|
796
|
+
|
797
|
+
keys = Map.depth_first_keys(enumerable)
|
798
|
+
|
799
|
+
keys.each do |key|
|
800
|
+
key.size.times do |i|
|
801
|
+
k = key.slice(0, i + 1)
|
802
|
+
level = k.size - 1
|
803
|
+
levels[level] ||= Array.new
|
804
|
+
last = levels[level].last
|
805
|
+
levels[level].push(k) unless last == k
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
levels.each do |level|
|
810
|
+
level.each do |key|
|
811
|
+
val = enumerable.get(key)
|
812
|
+
block ? block.call(key, val) : accum.push([key, val])
|
813
|
+
end
|
814
|
+
end
|
815
|
+
|
816
|
+
block ? enumerable : accum
|
817
|
+
end
|
818
|
+
|
819
|
+
def Map.keys_for(enumerable)
|
820
|
+
keys = enumerable.respond_to?(:keys) ? enumerable.keys : Array.new(enumerable.size){|i| i}
|
821
|
+
end
|
822
|
+
|
739
823
|
def depth_first_each(*args, &block)
|
740
824
|
Map.depth_first_each(enumerable=self, *args, &block)
|
741
825
|
end
|
826
|
+
|
827
|
+
def depth_first_keys(*args, &block)
|
828
|
+
Map.depth_first_keys(enumerable=self, *args, &block)
|
829
|
+
end
|
830
|
+
|
831
|
+
def depth_first_values(*args, &block)
|
832
|
+
Map.depth_first_values(enumerable=self, *args, &block)
|
833
|
+
end
|
834
|
+
|
835
|
+
def breadth_first_each(*args, &block)
|
836
|
+
Map.breadth_first_each(enumerable=self, *args, &block)
|
837
|
+
end
|
838
|
+
|
839
|
+
def contains(other)
|
840
|
+
other = other.is_a?(Hash) ? Map.coerce(other) : other
|
841
|
+
breadth_first_each{|key, value| return true if value == other}
|
842
|
+
return false
|
843
|
+
end
|
844
|
+
alias_method 'contains?', 'contains'
|
742
845
|
end
|
743
846
|
|
744
847
|
module Kernel
|
data/map.gemspec
CHANGED
data/test/map_test.rb
CHANGED
@@ -371,6 +371,65 @@ Testing Map do
|
|
371
371
|
assert{ each_pair = ['a', 'b', 'c', nil] }
|
372
372
|
end
|
373
373
|
|
374
|
+
testing 'that maps support breath_first_each' do
|
375
|
+
map = Map[
|
376
|
+
'hash' , {'x' => 'y'},
|
377
|
+
'nested hash' , {'nested' => {'a' => 'b'}},
|
378
|
+
'array' , [0, 1, 2],
|
379
|
+
'nested array' , [[3], [4], [5]],
|
380
|
+
'string' , '42'
|
381
|
+
]
|
382
|
+
|
383
|
+
accum = []
|
384
|
+
Map.breadth_first_each(map){|k, v| accum.push([k, v])}
|
385
|
+
expected =
|
386
|
+
[[["hash"], {"x"=>"y"}],
|
387
|
+
[["nested hash"], {"nested"=>{"a"=>"b"}}],
|
388
|
+
[["array"], [0, 1, 2]],
|
389
|
+
[["nested array"], [[3], [4], [5]]],
|
390
|
+
[["string"], "42"],
|
391
|
+
[["hash", "x"], "y"],
|
392
|
+
[["nested hash", "nested"], {"a"=>"b"}],
|
393
|
+
[["array", 0], 0],
|
394
|
+
[["array", 1], 1],
|
395
|
+
[["array", 2], 2],
|
396
|
+
[["nested array", 0], [3]],
|
397
|
+
[["nested array", 1], [4]],
|
398
|
+
[["nested array", 2], [5]],
|
399
|
+
[["nested hash", "nested", "a"], "b"],
|
400
|
+
[["nested array", 0, 0], 3],
|
401
|
+
[["nested array", 1, 0], 4],
|
402
|
+
[["nested array", 2, 0], 5]]
|
403
|
+
end
|
404
|
+
|
405
|
+
testing 'that maps have a needle-in-a-haystack like #contains? method' do
|
406
|
+
haystack = Map[
|
407
|
+
'hash' , {'x' => 'y'},
|
408
|
+
'nested hash' , {'nested' => {'a' => 'b'}},
|
409
|
+
'array' , [0, 1, 2],
|
410
|
+
'nested array' , [[3], [4], [5]],
|
411
|
+
'string' , '42'
|
412
|
+
]
|
413
|
+
|
414
|
+
needles = [
|
415
|
+
{'x' => 'y'},
|
416
|
+
{'nested' => {'a' => 'b'}},
|
417
|
+
{'a' => 'b'},
|
418
|
+
[0,1,2],
|
419
|
+
[[3], [4], [5]],
|
420
|
+
[3],
|
421
|
+
[4],
|
422
|
+
[5],
|
423
|
+
'42',
|
424
|
+
0,1,2,
|
425
|
+
3,4,5
|
426
|
+
]
|
427
|
+
|
428
|
+
needles.each do |needle|
|
429
|
+
assert{ haystack.contains?(needle) }
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
374
433
|
testing 'that #update and #replace accept map-ish objects' do
|
375
434
|
o = Object.new
|
376
435
|
def o.to_map() {:k => :v} end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: map
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 47
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 2.
|
8
|
+
- 8
|
9
|
+
- 0
|
10
|
+
version: 2.8.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ara T. Howard
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-04-08 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|