multimap 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []