multimap 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4db890b8870e719e47f7c20df596bd23d66b1de8
4
+ data.tar.gz: ca07f1b4792dbbdfb4872dadab843291c0bcc351
5
+ SHA512:
6
+ metadata.gz: acfec209b5d217b7123c7c80ad360cbed3062e22b1e400408191c9d026e417d40d9c47ae423741709c5a253afb814cfb9365bb23e061caeb734f0edae70dbd0d
7
+ data.tar.gz: dd890003da0062e7796203b12492e3d02a5d5ed2c5e7ce53bb27921175b3ed126bd988c44ef74dbd82c6a7a21aa9a0c590cecc6e3c2831e2e955337d3a826617
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Joshua Peek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ = Multimap
2
+
3
+ A Ruby multimap implementation that also includes multiset and nested multimap implementations.
4
+
5
+ == Example
6
+
7
+ require 'multimap'
8
+
9
+ map = Multimap.new
10
+ map["a"] = 100
11
+ map["b"] = 200
12
+ map["a"] = 300
13
+
14
+ map["a"] # -> [100, 300]
15
+ map["b"] # -> [200]
16
+ map.keys # -> #<Multiset: {"a", "a", "b"}>
@@ -0,0 +1,6 @@
1
+ if RUBY_PLATFORM == 'java'
2
+ File.open('Makefile', 'w') { |f| f.puts("install:\n\t$(echo Skipping native extensions)") }
3
+ else
4
+ require 'mkmf'
5
+ create_makefile('nested_multimap_ext')
6
+ end
@@ -0,0 +1,24 @@
1
+ #include "ruby.h"
2
+
3
+ VALUE cNestedMultimap;
4
+
5
+ static VALUE rb_nested_multimap_aref(int argc, VALUE *argv, VALUE self)
6
+ {
7
+ int i;
8
+ VALUE r, h;
9
+
10
+ for (i = 0, r = self; rb_obj_is_kind_of(r, cNestedMultimap) == Qtrue; i++) {
11
+ h = rb_funcall(r, rb_intern("_internal_hash"), 0);
12
+ Check_Type(h, T_HASH);
13
+ r = (i < argc) ? rb_hash_aref(h, argv[i]) : RHASH(h)->ifnone;
14
+ }
15
+
16
+ return r;
17
+ }
18
+
19
+ void Init_nested_multimap_ext() {
20
+ cNestedMultimap = rb_const_get(rb_cObject, rb_intern("NestedMultimap"));
21
+ // rb_funcall(cNestedMultimap, rb_intern("remove_method"), 1, rb_intern("[]"));
22
+ rb_eval_string("NestedMultimap.send(:remove_method, :[])");
23
+ rb_define_method(cNestedMultimap, "[]", rb_nested_multimap_aref, -1);
24
+ }
@@ -0,0 +1,569 @@
1
+ require 'forwardable'
2
+ require 'multiset'
3
+
4
+ # Multimap is a generalization of a map or associative array
5
+ # abstract data type in which more than one value may be associated
6
+ # with and returned for a given key.
7
+ #
8
+ # == Example
9
+ #
10
+ # require 'multimap'
11
+ # map = Multimap.new
12
+ # map["a"] = 100
13
+ # map["b"] = 200
14
+ # map["a"] = 300
15
+ # map["a"] # -> [100, 300]
16
+ # map["b"] # -> [200]
17
+ # map.keys # -> #<Multiset: {a, a, b}>
18
+ class Multimap
19
+ extend Forwardable
20
+
21
+ include Enumerable
22
+
23
+ # call-seq:
24
+ # Multimap[ [key =>|, value]* ] => multimap
25
+ #
26
+ # Creates a new multimap populated with the given objects.
27
+ #
28
+ # Multimap["a", 100, "b", 200] #=> {"a"=>[100], "b"=>[200]}
29
+ # Multimap["a" => 100, "b" => 200] #=> {"a"=>[100], "b"=>[200]}
30
+ def self.[](*args)
31
+ default = []
32
+
33
+ if args.size == 2 && args.last.is_a?(Hash)
34
+ default = args.shift
35
+ elsif !args.first.is_a?(Hash) && args.size % 2 == 1
36
+ default = args.shift
37
+ end
38
+
39
+ if args.size == 1 && args.first.is_a?(Hash)
40
+ args[0] = args.first.inject({}) { |hash, (key, value)|
41
+ unless value.is_a?(default.class)
42
+ value = (default.dup << value)
43
+ end
44
+ hash[key] = value
45
+ hash
46
+ }
47
+ else
48
+ index = 0
49
+ args.map! { |value|
50
+ unless index % 2 == 0 || value.is_a?(default.class)
51
+ value = (default.dup << value)
52
+ end
53
+ index += 1
54
+ value
55
+ }
56
+ end
57
+
58
+ map = new
59
+ map.instance_variable_set(:@hash, Hash[*args])
60
+ map.default = default
61
+ map
62
+ end
63
+
64
+ # call-seq:
65
+ # Multimap.new => multimap
66
+ # Multimap.new(default) => multimap
67
+ #
68
+ # Returns a new, empty multimap.
69
+ #
70
+ # map = Multimap.new(Set.new)
71
+ # h["a"] = 100
72
+ # h["b"] = 200
73
+ # h["a"] #=> [100].to_set
74
+ # h["c"] #=> [].to_set
75
+ def initialize(default = [])
76
+ @hash = Hash.new(default)
77
+ end
78
+
79
+ def initialize_copy(original) #:nodoc:
80
+ @hash = Hash.new(original.default.dup)
81
+ original._internal_hash.each_pair do |key, container|
82
+ @hash[key] = container.dup
83
+ end
84
+ end
85
+
86
+ def_delegators :@hash, :clear, :default, :default=, :empty?,
87
+ :fetch, :has_key?, :key?
88
+
89
+ # Retrieves the <i>value</i> object corresponding to the
90
+ # <i>*keys</i> object.
91
+ def [](key)
92
+ @hash[key]
93
+ end
94
+
95
+ # call-seq:
96
+ # map[key] = value => value
97
+ # map.store(key, value) => value
98
+ #
99
+ # Associates the value given by <i>value</i> with the key
100
+ # given by <i>key</i>. Unlike a regular hash, multiple can be
101
+ # assoicated with the same value.
102
+ #
103
+ # map = Multimap["a" => 100, "b" => 200]
104
+ # map["a"] = 9
105
+ # map["c"] = 4
106
+ # map #=> {"a" => [100, 9], "b" => [200], "c" => [4]}
107
+ def store(key, value)
108
+ update_container(key) do |container|
109
+ container << value
110
+ container
111
+ end
112
+ end
113
+ alias_method :[]=, :store
114
+
115
+ # call-seq:
116
+ # map.delete(key, value) => value
117
+ # map.delete(key) => value
118
+ #
119
+ # Deletes and returns a key-value pair from <i>map</i>. If only
120
+ # <i>key</i> is given, all the values matching that key will be
121
+ # deleted.
122
+ #
123
+ # map = Multimap["a" => 100, "b" => [200, 300]]
124
+ # map.delete("b", 300) #=> 300
125
+ # map.delete("a") #=> [100]
126
+ def delete(key, value = nil)
127
+ if value
128
+ @hash[key].delete(value)
129
+ else
130
+ @hash.delete(key)
131
+ end
132
+ end
133
+
134
+ # call-seq:
135
+ # map.each { |key, value| block } => map
136
+ #
137
+ # Calls <i>block</i> for each key/value pair in <i>map</i>, passing
138
+ # the key and value to the block as a two-element array.
139
+ #
140
+ # map = Multimap["a" => 100, "b" => [200, 300]]
141
+ # map.each { |key, value| puts "#{key} is #{value}" }
142
+ #
143
+ # <em>produces:</em>
144
+ #
145
+ # a is 100
146
+ # b is 200
147
+ # b is 300
148
+ def each
149
+ each_pair do |key, value|
150
+ yield [key, value]
151
+ end
152
+ end
153
+
154
+ # call-seq:
155
+ # map.each_association { |key, container| block } => map
156
+ #
157
+ # Calls <i>block</i> once for each key/container in <i>map</i>, passing
158
+ # the key and container to the block as parameters.
159
+ #
160
+ # map = Multimap["a" => 100, "b" => [200, 300]]
161
+ # map.each_association { |key, container| puts "#{key} is #{container}" }
162
+ #
163
+ # <em>produces:</em>
164
+ #
165
+ # a is [100]
166
+ # b is [200, 300]
167
+ def each_association(&block)
168
+ @hash.each_pair(&block)
169
+ end
170
+
171
+ # call-seq:
172
+ # map.each_container { |container| block } => map
173
+ #
174
+ # Calls <i>block</i> for each container in <i>map</i>, passing the
175
+ # container as a parameter.
176
+ #
177
+ # map = Multimap["a" => 100, "b" => [200, 300]]
178
+ # map.each_container { |container| puts container }
179
+ #
180
+ # <em>produces:</em>
181
+ #
182
+ # [100]
183
+ # [200, 300]
184
+ def each_container
185
+ each_association do |_, container|
186
+ yield container
187
+ end
188
+ end
189
+
190
+ # call-seq:
191
+ # map.each_key { |key| block } => map
192
+ #
193
+ # Calls <i>block</i> for each key in <i>hsh</i>, passing the key
194
+ # as a parameter.
195
+ #
196
+ # map = Multimap["a" => 100, "b" => [200, 300]]
197
+ # map.each_key { |key| puts key }
198
+ #
199
+ # <em>produces:</em>
200
+ #
201
+ # a
202
+ # b
203
+ # b
204
+ def each_key
205
+ each_pair do |key, _|
206
+ yield key
207
+ end
208
+ end
209
+
210
+ # call-seq:
211
+ # map.each_pair { |key_value_array| block } => map
212
+ #
213
+ # Calls <i>block</i> for each key/value pair in <i>map</i>,
214
+ # passing the key and value as parameters.
215
+ #
216
+ # map = Multimap["a" => 100, "b" => [200, 300]]
217
+ # map.each_pair { |key, value| puts "#{key} is #{value}" }
218
+ #
219
+ # <em>produces:</em>
220
+ #
221
+ # a is 100
222
+ # b is 200
223
+ # b is 300
224
+ def each_pair
225
+ each_association do |key, values|
226
+ values.each do |value|
227
+ yield key, value
228
+ end
229
+ end
230
+ end
231
+
232
+ # call-seq:
233
+ # map.each_value { |value| block } => map
234
+ #
235
+ # Calls <i>block</i> for each key in <i>map</i>, passing the
236
+ # value as a parameter.
237
+ #
238
+ # map = Multimap["a" => 100, "b" => [200, 300]]
239
+ # map.each_value { |value| puts value }
240
+ #
241
+ # <em>produces:</em>
242
+ #
243
+ # 100
244
+ # 200
245
+ # 300
246
+ def each_value
247
+ each_pair do |_, value|
248
+ yield value
249
+ end
250
+ end
251
+
252
+ def ==(other) #:nodoc:
253
+ case other
254
+ when Multimap
255
+ @hash == other._internal_hash
256
+ else
257
+ @hash == other
258
+ end
259
+ end
260
+
261
+ def eql?(other) #:nodoc:
262
+ case other
263
+ when Multimap
264
+ @hash.eql?(other._internal_hash)
265
+ else
266
+ @hash.eql?(other)
267
+ end
268
+ end
269
+
270
+ def freeze #:nodoc:
271
+ each_container { |container| container.freeze }
272
+ default.freeze
273
+ super
274
+ end
275
+
276
+ # call-seq:
277
+ # map.has_value?(value) => true or false
278
+ # map.value?(value) => true or false
279
+ #
280
+ # Returns <tt>true</tt> if the given value is present for any key
281
+ # in <i>map</i>.
282
+ #
283
+ # map = Multimap["a" => 100, "b" => [200, 300]]
284
+ # map.has_value?(300) #=> true
285
+ # map.has_value?(999) #=> false
286
+ def has_value?(value)
287
+ values.include?(value)
288
+ end
289
+ alias_method :value?, :has_value?
290
+
291
+ # call-seq:
292
+ # map.index(value) => key
293
+ #
294
+ # Returns the key for a given value. If not found, returns
295
+ # <tt>nil</tt>.
296
+ #
297
+ # map = Multimap["a" => 100, "b" => [200, 300]]
298
+ # map.index(100) #=> "a"
299
+ # map.index(200) #=> "b"
300
+ # map.index(999) #=> nil
301
+ def index(value)
302
+ invert[value]
303
+ end
304
+
305
+ # call-seq:
306
+ # map.delete_if {| key, value | block } -> map
307
+ #
308
+ # Deletes every key-value pair from <i>map</i> for which <i>block</i>
309
+ # evaluates to <code>true</code>.
310
+ #
311
+ # map = Multimap["a" => 100, "b" => [200, 300]]
312
+ # map.delete_if {|key, value| value >= 300 }
313
+ # #=> Multimap["a" => 100, "b" => 200]
314
+ #
315
+ def delete_if
316
+ each_association do |key, container|
317
+ container.delete_if do |value|
318
+ yield [key, value]
319
+ end
320
+ end
321
+ self
322
+ end
323
+
324
+ # call-seq:
325
+ # map.reject {| key, value | block } -> map
326
+ #
327
+ # Same as <code>Multimap#delete_if</code>, but works on (and returns) a
328
+ # copy of the <i>map</i>. Equivalent to
329
+ # <code><i>map</i>.dup.delete_if</code>.
330
+ #
331
+ def reject(&block)
332
+ dup.delete_if(&block)
333
+ end
334
+
335
+ # call-seq:
336
+ # map.reject! {| key, value | block } -> map or nil
337
+ #
338
+ # Equivalent to <code>Multimap#delete_if</code>, but returns
339
+ # <code>nil</code> if no changes were made.
340
+ #
341
+ def reject!(&block)
342
+ old_size = size
343
+ delete_if(&block)
344
+ old_size == size ? nil : self
345
+ end
346
+
347
+ # call-seq:
348
+ # map.replace(other_map) => map
349
+ #
350
+ # Replaces the contents of <i>map</i> with the contents of
351
+ # <i>other_map</i>.
352
+ #
353
+ # map = Multimap["a" => 100, "b" => 200]
354
+ # map.replace({ "c" => 300, "d" => 400 })
355
+ # #=> Multimap["c" => 300, "d" => 400]
356
+ def replace(other)
357
+ case other
358
+ when Array
359
+ @hash.replace(self.class[self.default, *other])
360
+ when Hash
361
+ @hash.replace(self.class[self.default, other])
362
+ when self.class
363
+ @hash.replace(other)
364
+ else
365
+ raise ArgumentError
366
+ end
367
+ end
368
+
369
+ # call-seq:
370
+ # map.invert => multimap
371
+ #
372
+ # Returns a new multimap created by using <i>map</i>'s values as keys,
373
+ # and the keys as values.
374
+ #
375
+ # map = Multimap["n" => 100, "m" => 100, "d" => [200, 300]]
376
+ # map.invert #=> Multimap[100 => ["n", "m"], 200 => "d", 300 => "d"]
377
+ def invert
378
+ h = self.class.new(default.dup)
379
+ each_pair { |key, value| h[value] = key }
380
+ h
381
+ end
382
+
383
+ # call-seq:
384
+ # map.keys => multiset
385
+ #
386
+ # Returns a new +Multiset+ populated with the keys from this hash. See also
387
+ # <tt>Multimap#values</tt> and <tt>Multimap#containers</tt>.
388
+ #
389
+ # map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
390
+ # map.keys #=> Multiset.new(["a", "b", "b", "c"])
391
+ def keys
392
+ keys = Multiset.new
393
+ each_key { |key| keys << key }
394
+ keys
395
+ end
396
+
397
+ # Returns true if the given key is present in Multimap.
398
+ def include?(key)
399
+ keys.include?(key)
400
+ end
401
+ alias_method :member?, :include?
402
+
403
+ # call-seq:
404
+ # map.length => fixnum
405
+ # map.size => fixnum
406
+ #
407
+ # Returns the number of key-value pairs in the map.
408
+ #
409
+ # map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
410
+ # map.length #=> 4
411
+ # map.delete("a") #=> 100
412
+ # map.length #=> 3
413
+ def size
414
+ values.size
415
+ end
416
+ alias_method :length, :size
417
+
418
+ # call-seq:
419
+ # map.merge(other_map) => multimap
420
+ #
421
+ # Returns a new multimap containing the contents of <i>other_map</i> and
422
+ # the contents of <i>map</i>.
423
+ #
424
+ # map1 = Multimap["a" => 100, "b" => 200]
425
+ # map2 = Multimap["a" => 254, "c" => 300]
426
+ # map2.merge(map2) #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
427
+ # map1 #=> Multimap["a" => 100, "b" => 200]
428
+ def merge(other)
429
+ dup.update(other)
430
+ end
431
+
432
+ # call-seq:
433
+ # map.merge!(other_map) => multimap
434
+ # map.update(other_map) => multimap
435
+ #
436
+ # Adds each pair from <i>other_map</i> to <i>map</i>.
437
+ #
438
+ # map1 = Multimap["a" => 100, "b" => 200]
439
+ # map2 = Multimap["b" => 254, "c" => 300]
440
+ #
441
+ # map1.merge!(map2)
442
+ # #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
443
+ def update(other)
444
+ case other
445
+ when self.class
446
+ other.each_pair { |key, value| store(key, value) }
447
+ when Hash
448
+ update(self.class[self.default, other])
449
+ else
450
+ raise ArgumentError
451
+ end
452
+ self
453
+ end
454
+ alias_method :merge!, :update
455
+
456
+ # call-seq:
457
+ # map.select { |key, value| block } => multimap
458
+ #
459
+ # Returns a new Multimap consisting of the pairs for which the
460
+ # block returns true.
461
+ #
462
+ # map = Multimap["a" => 100, "b" => 200, "c" => 300]
463
+ # map.select { |k,v| k > "a" } #=> Multimap["b" => 200, "c" => 300]
464
+ # map.select { |k,v| v < 200 } #=> Multimap["a" => 100]
465
+ def select
466
+ inject(self.class.new) { |map, (key, value)|
467
+ map[key] = value if yield([key, value])
468
+ map
469
+ }
470
+ end
471
+
472
+ # call-seq:
473
+ # map.to_a => array
474
+ #
475
+ # Converts <i>map</i> to a nested array of [<i>key,
476
+ # value</i>] arrays.
477
+ #
478
+ # map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
479
+ # map.to_a #=> [["a", 100], ["b", 200], ["b", 300], ["c", 400]]
480
+ def to_a
481
+ ary = []
482
+ each_pair do |key, value|
483
+ ary << [key, value]
484
+ end
485
+ ary
486
+ end
487
+
488
+ # call-seq:
489
+ # map.to_hash => hash
490
+ #
491
+ # Converts <i>map</i> to a basic hash.
492
+ #
493
+ # map = Multimap["a" => 100, "b" => [200, 300]]
494
+ # map.to_hash #=> { "a" => [100], "b" => [200, 300] }
495
+ def to_hash
496
+ @hash.dup
497
+ end
498
+
499
+ # call-seq:
500
+ # map.containers => array
501
+ #
502
+ # Returns a new array populated with the containers from <i>map</i>. See
503
+ # also <tt>Multimap#keys</tt> and <tt>Multimap#values</tt>.
504
+ #
505
+ # map = Multimap["a" => 100, "b" => [200, 300]]
506
+ # map.containers #=> [[100], [200, 300]]
507
+ def containers
508
+ containers = []
509
+ each_container { |container| containers << container }
510
+ containers
511
+ end
512
+
513
+ # call-seq:
514
+ # map.values => array
515
+ #
516
+ # Returns a new array populated with the values from <i>map</i>. See
517
+ # also <tt>Multimap#keys</tt> and <tt>Multimap#containers</tt>.
518
+ #
519
+ # map = Multimap["a" => 100, "b" => [200, 300]]
520
+ # map.values #=> [100, 200, 300]
521
+ def values
522
+ values = []
523
+ each_value { |value| values << value }
524
+ values
525
+ end
526
+
527
+ # Return an array containing the values associated with the given keys.
528
+ def values_at(*keys)
529
+ @hash.values_at(*keys)
530
+ end
531
+
532
+ def marshal_dump #:nodoc:
533
+ @hash
534
+ end
535
+
536
+ def marshal_load(hash) #:nodoc:
537
+ @hash = hash
538
+ end
539
+
540
+ def to_yaml(opts = {}) #:nodoc:
541
+ YAML::quick_emit(self, opts) do |out|
542
+ out.map(taguri, to_yaml_style) do |map|
543
+ @hash.each do |k, v|
544
+ map.add(k, v)
545
+ end
546
+ map.add('__default__', @hash.default)
547
+ end
548
+ end
549
+ end
550
+
551
+ def yaml_initialize(tag, val) #:nodoc:
552
+ default = val.delete('__default__')
553
+ @hash = val
554
+ @hash.default = default
555
+ self
556
+ end
557
+
558
+ protected
559
+ def _internal_hash #:nodoc:
560
+ @hash
561
+ end
562
+
563
+ def update_container(key) #:nodoc:
564
+ container = @hash[key]
565
+ container = container.dup if container.equal?(default)
566
+ container = yield(container)
567
+ @hash[key] = container
568
+ end
569
+ end
@@ -0,0 +1,185 @@
1
+ require 'set'
2
+
3
+ # Multiset implements a collection of unordered values and
4
+ # allows duplicates.
5
+ #
6
+ # == Example
7
+ #
8
+ # require 'multiset'
9
+ # s1 = Multiset.new [1, 2] # -> #<Multiset: {1, 2}>
10
+ # s1.add(2) # -> #<Multiset: {1, 2, 2}>
11
+ # s1.merge([2, 6]) # -> #<Multiset: {1, 2, 2, 2, 3}>
12
+ # s1.multiplicity(2) # -> 3
13
+ # s1.multiplicity(3) # -> 1
14
+ class Multiset < Set
15
+ def initialize(*args, &block) #:nodoc:
16
+ @hash = Hash.new(0)
17
+ super
18
+ end
19
+
20
+ # Returns the number of times an element belongs to the multiset.
21
+ def multiplicity(e)
22
+ @hash[e]
23
+ end
24
+
25
+ # Returns the total number of elements in a multiset, including
26
+ # repeated memberships
27
+ def cardinality
28
+ @hash.inject(0) { |s, (e, m)| s += m }
29
+ end
30
+ alias_method :size, :cardinality
31
+ alias_method :length, :cardinality
32
+
33
+ # Converts the set to an array. The order of elements is uncertain.
34
+ def to_a
35
+ inject([]) { |ary, (key, _)| ary << key }
36
+ end
37
+
38
+ # Returns true if the set is a superset of the given set.
39
+ def superset?(set)
40
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
41
+ return false if cardinality < set.cardinality
42
+ set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
43
+ end
44
+
45
+ # Returns true if the set is a proper superset of the given set.
46
+ def proper_superset?(set)
47
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
48
+ return false if cardinality <= set.cardinality
49
+ set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
50
+ end
51
+
52
+ # Returns true if the set is a subset of the given set.
53
+ def subset?(set)
54
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
55
+ return false if set.cardinality < cardinality
56
+ all? { |o| multiplicity(o) <= set.multiplicity(o) }
57
+ end
58
+
59
+ # Returns true if the set is a proper subset of the given set.
60
+ def proper_subset?(set)
61
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
62
+ return false if set.cardinality <= cardinality
63
+ all? { |o| multiplicity(o) <= set.multiplicity(o) }
64
+ end
65
+
66
+ # Calls the given block once for each element in the set, passing
67
+ # the element as parameter. Returns an enumerator if no block is
68
+ # given.
69
+ def each
70
+ @hash.each_pair do |key, multiplicity|
71
+ multiplicity.times do
72
+ yield(key)
73
+ end
74
+ end
75
+ self
76
+ end
77
+
78
+ # Adds the given object to the set and returns self. Use +merge+ to
79
+ # add many elements at once.
80
+ def add(o)
81
+ @hash[o] ||= 0
82
+ @hash[o] += 1
83
+ self
84
+ end
85
+ alias << add
86
+
87
+ undef :add?
88
+
89
+ # Deletes all the identical object from the set and returns self.
90
+ # If +n+ is given, it will remove that amount of identical objects
91
+ # from the set. Use +subtract+ to delete many different items at
92
+ # once.
93
+ def delete(o, n = nil)
94
+ if n
95
+ @hash[o] ||= 0
96
+ @hash[o] -= n if @hash[o] > 0
97
+ @hash.delete(o) if @hash[o] == 0
98
+ else
99
+ @hash.delete(o)
100
+ end
101
+ self
102
+ end
103
+
104
+ undef :delete?
105
+
106
+ # Deletes every element of the set for which block evaluates to
107
+ # true, and returns self.
108
+ def delete_if
109
+ each { |o| delete(o) if yield(o) }
110
+ self
111
+ end
112
+
113
+ # Merges the elements of the given enumerable object to the set and
114
+ # returns self.
115
+ def merge(enum)
116
+ enum.each { |o| add(o) }
117
+ self
118
+ end
119
+
120
+ # Deletes every element that appears in the given enumerable object
121
+ # and returns self.
122
+ def subtract(enum)
123
+ enum.each { |o| delete(o, 1) }
124
+ self
125
+ end
126
+
127
+ # Returns a new set containing elements common to the set and the
128
+ # given enumerable object.
129
+ def &(enum)
130
+ s = dup
131
+ n = self.class.new
132
+ enum.each { |o|
133
+ if s.include?(o)
134
+ s.delete(o, 1)
135
+ n.add(o)
136
+ end
137
+ }
138
+ n
139
+ end
140
+ alias intersection &
141
+
142
+ # Returns a new set containing elements exclusive between the set
143
+ # and the given enumerable object. (set ^ enum) is equivalent to
144
+ # ((set | enum) - (set & enum)).
145
+ def ^(enum)
146
+ n = self.class.new(enum)
147
+ each { |o| n.include?(o) ? n.delete(o, 1) : n.add(o) }
148
+ n
149
+ end
150
+
151
+ # Returns true if two sets are equal. Two multisets are equal if
152
+ # they have the same cardinalities and each element has the same
153
+ # multiplicity in both sets. The equality of each element inside
154
+ # the multiset is defined according to Object#eql?.
155
+ def eql?(set)
156
+ return true if equal?(set)
157
+ set = self.class.new(set) unless set.is_a?(self.class)
158
+ return false unless cardinality == set.cardinality
159
+ superset?(set) && subset?(set)
160
+ end
161
+ alias_method :==, :eql?
162
+
163
+ def marshal_dump #:nodoc:
164
+ @hash
165
+ end
166
+
167
+ def marshal_load(hash) #:nodoc:
168
+ @hash = hash
169
+ end
170
+
171
+ def to_yaml(opts = {}) #:nodoc:
172
+ YAML::quick_emit(self, opts) do |out|
173
+ out.map(taguri, to_yaml_style) do |map|
174
+ @hash.each do |k, v|
175
+ map.add(k, v)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ def yaml_initialize(tag, val) #:nodoc:
182
+ @hash = val
183
+ self
184
+ end
185
+ end
@@ -0,0 +1,158 @@
1
+ require 'multimap'
2
+
3
+ # NestedMultimap allows values to be assoicated with a nested
4
+ # set of keys.
5
+ class NestedMultimap < Multimap
6
+ # call-seq:
7
+ # multimap[*keys] = value => value
8
+ # multimap.store(*keys, value) => value
9
+ #
10
+ # Associates the value given by <i>value</i> with multiple key
11
+ # given by <i>keys</i>.
12
+ #
13
+ # map = NestedMultimap.new
14
+ # map["a"] = 100
15
+ # map["a", "b"] = 101
16
+ # map["a"] = 102
17
+ # map #=> {"a"=>{"b"=>[100, 101, 102], default => [100, 102]}}
18
+ def store(*args)
19
+ keys = args
20
+ value = args.pop
21
+
22
+ raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
23
+
24
+ if keys.length > 1
25
+ update_container(keys.shift) do |container|
26
+ container = self.class.new(container) unless container.is_a?(self.class)
27
+ container[*keys] = value
28
+ container
29
+ end
30
+ elsif keys.length == 1
31
+ super(keys.first, value)
32
+ else
33
+ self << value
34
+ end
35
+ end
36
+ alias_method :[]=, :store
37
+
38
+ # call-seq:
39
+ # multimap << obj => multimap
40
+ #
41
+ # Pushes the given object on to the end of all the containers.
42
+ #
43
+ # map = NestedMultimap["a" => [100], "b" => [200, 300]]
44
+ # map << 300
45
+ # map["a"] #=> [100, 300]
46
+ # map["c"] #=> [300]
47
+ def <<(value)
48
+ @hash.each_value { |container| container << value }
49
+ self.default << value
50
+ self
51
+ end
52
+
53
+ # call-seq:
54
+ # multimap[*keys] => value
55
+ # multimap[key1, key2, key3] => value
56
+ #
57
+ # Retrieves the <i>value</i> object corresponding to the
58
+ # <i>*keys</i> object.
59
+ def [](*keys)
60
+ i, l, r, k = 0, keys.length, self, self.class
61
+ while r.is_a?(k)
62
+ r = i < l ? r._internal_hash[keys[i]] : r.default
63
+ i += 1
64
+ end
65
+ r
66
+ end
67
+
68
+ # call-seq:
69
+ # multimap.each_association { |key, container| block } => multimap
70
+ #
71
+ # Calls <i>block</i> once for each key/container in <i>map</i>, passing
72
+ # the key and container to the block as parameters.
73
+ #
74
+ # map = NestedMultimap.new
75
+ # map["a"] = 100
76
+ # map["a", "b"] = 101
77
+ # map["a"] = 102
78
+ # map["c"] = 200
79
+ # map.each_association { |key, container| puts "#{key} is #{container}" }
80
+ #
81
+ # <em>produces:</em>
82
+ #
83
+ # ["a", "b"] is [100, 101, 102]
84
+ # "c" is [200]
85
+ def each_association
86
+ super() do |key, container|
87
+ if container.respond_to?(:each_association)
88
+ container.each_association do |nested_key, value|
89
+ yield [key, nested_key].flatten, value
90
+ end
91
+ else
92
+ yield key, container
93
+ end
94
+ end
95
+ end
96
+
97
+ # call-seq:
98
+ # multimap.each_container_with_default { |container| block } => map
99
+ #
100
+ # Calls <i>block</i> for every container in <i>map</i> including
101
+ # the default, passing the container as a parameter.
102
+ #
103
+ # map = NestedMultimap.new
104
+ # map["a"] = 100
105
+ # map["a", "b"] = 101
106
+ # map["a"] = 102
107
+ # map.each_container_with_default { |container| puts container }
108
+ #
109
+ # <em>produces:</em>
110
+ #
111
+ # [100, 101, 102]
112
+ # [100, 102]
113
+ # []
114
+ def each_container_with_default(&block)
115
+ @hash.each_value do |container|
116
+ iterate_over_container(container, &block)
117
+ end
118
+ iterate_over_container(default, &block)
119
+ self
120
+ end
121
+
122
+ # call-seq:
123
+ # multimap.containers_with_default => array
124
+ #
125
+ # Returns a new array populated with all the containers from
126
+ # <i>map</i> including the default.
127
+ #
128
+ # map = NestedMultimap.new
129
+ # map["a"] = 100
130
+ # map["a", "b"] = 101
131
+ # map["a"] = 102
132
+ # map.containers_with_default #=> [[100, 101, 102], [100, 102], []]
133
+ def containers_with_default
134
+ containers = []
135
+ each_container_with_default { |container| containers << container }
136
+ containers
137
+ end
138
+
139
+ def inspect #:nodoc:
140
+ super.gsub(/\}$/, ", default => #{default.inspect}}")
141
+ end
142
+
143
+ private
144
+ def iterate_over_container(container)
145
+ if container.respond_to?(:each_container_with_default)
146
+ container.each_container_with_default do |value|
147
+ yield value
148
+ end
149
+ else
150
+ yield container
151
+ end
152
+ end
153
+ end
154
+
155
+ begin
156
+ require 'nested_multimap_ext'
157
+ rescue LoadError
158
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multimap
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.3
5
+ platform: ruby
6
+ authors:
7
+ - Joshua Peek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |2
14
+ Multimap includes a Ruby multimap implementation
15
+ email: josh@joshpeek.com
16
+ executables: []
17
+ extensions:
18
+ - ext/extconf.rb
19
+ extra_rdoc_files:
20
+ - README.rdoc
21
+ - LICENSE
22
+ files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ - ext/extconf.rb
26
+ - ext/nested_multimap_ext.c
27
+ - lib/multimap.rb
28
+ - lib/multiset.rb
29
+ - lib/nested_multimap.rb
30
+ homepage: http://github.com/doxavore/multimap
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
34
+ post_install_message:
35
+ rdoc_options:
36
+ - "--title"
37
+ - Multimap
38
+ - "--main"
39
+ - README.rdoc
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 2.2.2
55
+ signing_key:
56
+ specification_version: 4
57
+ summary: Ruby multimap implementation
58
+ test_files: []