Class: Xqsr3::Containers::MultiMap
- Inherits:
-
Object
- Object
- Xqsr3::Containers::MultiMap
- Includes:
- Enumerable
- Defined in:
- lib/xqsr3/containers/multi_map.rb
Overview
Hash-like class that stores mapped values in arrays
Class Method Summary collapse
-
.[](*args) ⇒ Object
Creates an instance from the given arguments.
Instance Method Summary collapse
-
#==(rhs) ⇒ Object
Compares the instance for equality against
rhs. -
#[](key) ⇒ Object
Obtains the values, if any, for the given key; returns
nilif no values are stored. -
#[]=(key, values) ⇒ Object
Adds/assigns a new key+values pair.
-
#assoc(key) ⇒ Object
Searches the instance comparing each element with
key, returning the mapped values array if found, ornilif not. -
#clear ⇒ Object
Removes all elements from the instance.
-
#count ⇒ Object
The total number of instances recorded.
-
#delete(key) ⇒ Object
Deletes all values mapped with the given
key. -
#each(*defaults) ⇒ Object
Calls block once for each key-value pair, passing the key and each of its values in turn.
-
#each_key ⇒ Object
Calls block once for each key in the instance, passing the key.
-
#each_unflattened ⇒ Object
Calls block once for each key-values pair, passing the key and its values array.
-
#each_unflattened_with_index ⇒ Object
Calls block once for each key-values pair, passing the key and its values array and a key index.
-
#each_value ⇒ Object
Calls block once for each value in the instance, passing the value.
-
#each_with_index ⇒ Object
Calls block once for each key-values, passing the key and each of its values and a value index.
-
#empty? ⇒ Boolean
Returns
trueif instance contains no elements;falseotherwise. -
#eql?(rhs) ⇒ Boolean
Returns
trueifrhsis an instance ofMultiMapand contains the same elements and their counts;falseotherwise. -
#fetch(key, default = (default_parameter_defaulted_ = true; nil), &block) ⇒ Object
Returns the values associated with the given key.
-
#flatten ⇒ Object
Returns the equivalent flattened form of the instance.
-
#has_key?(key) ⇒ Boolean
(also: #include?, #key?, #member?)
Returns
trueif an element with the givenkeyis in the map;falseotherwise. -
#has_value?(value) ⇒ Boolean
Returns
trueif any key has the givenvalue;falseotherwise. -
#has_values?(values) ⇒ Boolean
Returns
trueif any key has the givenvalues;falseotherwise. -
#initialize ⇒ MultiMap
constructor
Initialises an instance.
-
#initialize_copy(other) ⇒ Object
Deep-copies
@inner(and each values array) after +dup+/+clone+. -
#key(*values) ⇒ Object
Returns the key for the given value(s).
-
#keys ⇒ Object
An array of all keys in the instance.
-
#length ⇒ Object
(also: #size)
The number of elements in the map.
-
#merge(other) ⇒ Object
See #merge.
-
#merge!(other) ⇒ Object
See #merge!.
-
#multi_merge(other) ⇒ Object
Returns a new instance containing a merging of the current instance and the
otherinstance. -
#multi_merge!(other) ⇒ Object
Merges the contents of
otherinto the current instance. -
#push(key, *values) ⇒ Object
Pushes the given
keyandvalues. -
#shift ⇒ Object
Removes a key-value pair from the instance and return as a two-item array.
-
#store(key, *values) ⇒ Object
Causes an element with the given
keyandvaluesto be stored. -
#strict_merge(other) ⇒ Object
Returns a new instance containing a merging of the current instance and the
otherinstance. -
#strict_merge!(other) ⇒ Object
Merges the contents of
otherinto the current instance. -
#to_a ⇒ Object
Converts instance to an array of
[key,value]pairs. -
#to_h ⇒ Object
Obtains reference to internal hash instance (which must not be modified).
-
#to_hash ⇒ Object
Obtains equivalent hash to instance (independent copy).
-
#to_s ⇒ Object
A string-form of the instance.
-
#values ⇒ Object
An array of all values in the instance.
-
#values_unflattened ⇒ Object
An array of all sets of values in the instance.
Methods included from Enumerable
#collect_with_index, #detect_map, #unique
Constructor Details
Class Method Details
.[](*args) ⇒ Object
Creates an instance from the given arguments
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/xqsr3/containers/multi_map.rb', line 60 def self.[] *args return self.new if 0 == args.length if 1 == args.length arg = args[0] case arg when ::NilClass return self.new when ::Hash mm = self.new arg.each do |k, v| raise ArgumentError, "mapped elements in hashes must be arrays, #{v.class} given" unless v.kind_of? ::Array mm.store k, *v end return mm when ::Array # accepted forms: # # 1. Empty array # 2. Array exclusively of arrays # 1. Empty array return self.new if arg.empty? # 2. Array exclusively of arrays if arg.all? { |el| ::Array === el } h = Hash.new { |hash, key| hash[key] = [] } arg.each do |ar| raise ArgumentError, "cannot pass an empty array in array of arrays initialiser" if ar.empty? key = ar[0] values = ar[1 ... ar.size] values.each { |value| h[key] << value } end return self.[](h) end raise ArgumentError, "array parameter not in an accepted form for subscript initialisation" else return self.[] arg.to_hash if arg.respond_to? :to_hash raise TypeError, "given argument is neither a #{::Hash} nor an #{::Array} and does not respond to the to_hash method" end else # treat all other argument permutations as having passed in an array return self.[] [ *args ] end end |
Instance Method Details
#==(rhs) ⇒ Object
Compares the instance for equality against rhs
Signature
-
Parameters:
rhs(+nil+,Hash,MultiMap) The instance to compare against;
Exceptions
TypeErrorifrhsis not of the required type(s)
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/xqsr3/containers/multi_map.rb', line 189 def == rhs case rhs when ::NilClass return false when ::Hash return rhs.size == @inner.size && rhs == @inner when self.class return rhs.size == self.size && rhs == @inner else raise TypeError, "can compare #{self.class} only to instances of #{self.class} and #{::Hash}, but #{rhs.class} given" end false end |
#[](key) ⇒ Object
Obtains the values, if any, for the given key; returns nil if no
values are stored
154 155 156 157 |
# File 'lib/xqsr3/containers/multi_map.rb', line 154 def [] key return @inner[key] end |
#[]=(key, values) ⇒ Object
Adds/assigns a new key+values pair. Equivalent to
store(key, *values)
Signature
-
Parameters:
keyThe element key;values(+Array+) The values to be associated with the key;
Exceptions
TypeErrorifvaluesis not an array
171 172 173 174 175 176 177 178 |
# File 'lib/xqsr3/containers/multi_map.rb', line 171 def []= key, values values = [] if values.nil? raise TypeError, "values must be an array, but #{values.class} given" unless values.kind_of? ::Array store key, *values end |
#assoc(key) ⇒ Object
Searches the instance comparing each element with key, returning the
mapped values array if found, or nil if not
207 208 209 210 |
# File 'lib/xqsr3/containers/multi_map.rb', line 207 def assoc key @inner.assoc key end |
#clear ⇒ Object
Removes all elements from the instance
213 214 215 216 |
# File 'lib/xqsr3/containers/multi_map.rb', line 213 def clear @inner.clear end |
#count ⇒ Object
The total number of instances recorded
219 220 221 222 |
# File 'lib/xqsr3/containers/multi_map.rb', line 219 def count @inner.each_value.map { |ar| ar.size}.inject(0, :+) end |
#delete(key) ⇒ Object
Deletes all values mapped with the given key
Signature
-
Parameters:
keyThe key to delete
230 231 232 233 |
# File 'lib/xqsr3/containers/multi_map.rb', line 230 def delete key @inner.delete key end |
#each(*defaults) ⇒ Object
Calls block once for each key-value pair, passing the key and each
of its values in turn. If the values for a given key are empty and
defaults is not empty, the block is invoked for that key (with
defaults[0]) once
Exceptions
ArgumentErrorif more than 1defaultsis provided, or no block is given
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/xqsr3/containers/multi_map.rb', line 242 def each *defaults raise ArgumentError, "may only supply 0 or 1 defaults" if defaults.size > 1 raise ArgumentError, 'block is required' unless block_given? @inner.each do |key, values| if values.empty? && !defaults.empty? yield key, defaults[0] next end values.each { |value| yield key, value } end end |
#each_key ⇒ Object
Calls block once for each key in the instance, passing the key. If no block is provided, an enumerator is returned
262 263 264 265 266 267 |
# File 'lib/xqsr3/containers/multi_map.rb', line 262 def each_key return @inner.each_key unless block_given? @inner.each_key { |key| yield key } end |
#each_unflattened ⇒ Object
Calls block once for each key-values pair, passing the key and its values array. If no block is provided, an enumerator is returned
271 272 273 274 275 276 |
# File 'lib/xqsr3/containers/multi_map.rb', line 271 def each_unflattened return @inner.each unless block_given? @inner.each { |key, value| yield key, value } end |
#each_unflattened_with_index ⇒ Object
Calls block once for each key-values pair, passing the key and its values array and a key index. If no block is provided, an enumerator is returned
281 282 283 284 285 286 |
# File 'lib/xqsr3/containers/multi_map.rb', line 281 def each_unflattened_with_index return @inner.each_with_index unless block_given? @inner.each_with_index { |kv, index| yield kv, index } end |
#each_value ⇒ Object
Calls block once for each value in the instance, passing the value. If no block is provided, an enumerator is returned
290 291 292 293 294 295 296 297 298 |
# File 'lib/xqsr3/containers/multi_map.rb', line 290 def each_value return @inner.each_value unless block_given? @inner.each do |key, values| values.each { |value| yield value } end end |
#each_with_index ⇒ Object
Calls block once for each key-values, passing the key and each of its values and a value index
Exceptions
ArgumentErrorif no block is given
305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/xqsr3/containers/multi_map.rb', line 305 def each_with_index raise ArgumentError, 'block is required' unless block_given? index = 0 self.each do |key, value| yield key, value, index index += 1 end end |
#empty? ⇒ Boolean
Returns true if instance contains no elements; false otherwise
319 320 321 322 |
# File 'lib/xqsr3/containers/multi_map.rb', line 319 def empty? @inner.empty? end |
#eql?(rhs) ⇒ Boolean
Returns true if rhs is an instance of MultiMap and contains
the same elements and their counts; false otherwise
326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/xqsr3/containers/multi_map.rb', line 326 def eql? rhs case rhs when self.class return self == rhs else return false end end |
#fetch(key, default = (default_parameter_defaulted_ = true; nil), &block) ⇒ Object
Returns the values associated with the given key
Signature
-
Parameters:
keyThe keydefaultThe default value
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/xqsr3/containers/multi_map.rb', line 345 def fetch key, default = (default_parameter_defaulted_ = true; nil), &block unless default_parameter_defaulted_ case default when ::NilClass, ::Array ; else raise TypeError, "default parameter ('#{default}') must be of type #{::Array}, but was of type #{default.class}" end end unless @inner.has_key? key return default unless default_parameter_defaulted_ if block_given? r = nil case block.arity when 0 r = yield when 1 r = yield key else raise ArgumentError, "given block must take a single parameter - #{block.arity} given" end case r when ::Array ; else raise ArgumentError, "given block must return a value of type #{::Array} or one convertible implicitly to such" unless r.respond_to? :to_ary r = r.to_ary end return r end raise KeyError, "given key '#{key}' (#{key.class}) does not exist" end @inner.fetch key end |
#flatten ⇒ Object
Returns the equivalent flattened form of the instance
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 |
# File 'lib/xqsr3/containers/multi_map.rb', line 400 def flatten r = [] @inner.each do |key, values| if values.empty? r << key << [] else values.each do |value| r << key << value end end end r end |
#has_key?(key) ⇒ Boolean Also known as: include?, key?, member?
Returns true if an element with the given key is in the map; false
otherwise
423 424 425 426 |
# File 'lib/xqsr3/containers/multi_map.rb', line 423 def has_key? key @inner.has_key? key end |
#has_value?(value) ⇒ Boolean
Returns true if any key has the given value; false otherwise
Signature
-
Parameters:
valueThe value for which to search
444 445 446 447 448 449 450 451 452 |
# File 'lib/xqsr3/containers/multi_map.rb', line 444 def has_value? value @inner.each do |k, vals| return true if vals.include? value end false end |
#has_values?(values) ⇒ Boolean
Returns true if any key has the given values; false otherwise
Signature
-
Parameters:
values(+Array+) The values for which to search;
Exceptions
TypeErrorifvalueis not an Array;
463 464 465 466 467 468 |
# File 'lib/xqsr3/containers/multi_map.rb', line 463 def has_values? values raise TypeError, "'values' parameter must be of type #{::Array}" unless Array === values @inner.has_value? values end |
#initialize_copy(other) ⇒ Object
Deep-copies @inner (and each values array) after +dup+/+clone+
141 142 143 144 145 146 147 148 149 150 |
# File 'lib/xqsr3/containers/multi_map.rb', line 141 def initialize_copy other @merge_is_multi = other.instance_variable_get(:@merge_is_multi) @inner = {} other.instance_variable_get(:@inner).each do |k, vals| @inner[k] = vals.dup end end |
#key(*values) ⇒ Object
Returns the key for the given value(s)
Signature
-
Parameters:
values(+Array+) The value(s) for which to search;
If a single value is specified, the entries in the instance are searched first for an exact match to all (1) value(s); if that fails, then the first key with a values containing the given value is returned
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'lib/xqsr3/containers/multi_map.rb', line 481 def key *values case values.size when 0 return nil when 1 i = nil @inner.each do |k, vals| return k if vals == values if i.nil? i = k if vals.include? values[0] end end return i else @inner.each do |key, vals| return key if vals == values end return nil end end |
#keys ⇒ Object
An array of all keys in the instance
433 434 435 436 |
# File 'lib/xqsr3/containers/multi_map.rb', line 433 def keys @inner.keys end |
#length ⇒ Object Also known as: size
The number of elements in the map
514 515 516 517 |
# File 'lib/xqsr3/containers/multi_map.rb', line 514 def length @inner.size end |
#merge(other) ⇒ Object
See #merge
648 649 650 651 652 653 654 655 656 657 |
# File 'lib/xqsr3/containers/multi_map.rb', line 648 def merge other if @merge_is_multi multi_merge other else strict_merge other end end |
#merge!(other) ⇒ Object
See #merge!
660 661 662 663 664 665 666 667 668 669 |
# File 'lib/xqsr3/containers/multi_map.rb', line 660 def merge! other if @merge_is_multi multi_merge! other else strict_merge! other end end |
#multi_merge(other) ⇒ Object
Returns a new instance containing a merging of the current instance and
the other instance
NOTE: where any key is found in both merging instances the values resulting will be a concatenation of the sets of values
Signature
-
Parameters:
other(+MultiMap+,Hash) The instance from which to merge;
Exceptions
TypeErrorRaised ifotheris not aMultiMapor aHash;
532 533 534 535 536 537 538 539 540 |
# File 'lib/xqsr3/containers/multi_map.rb', line 532 def multi_merge other mm = self.class.new mm.merge! self mm.merge! other mm end |
#multi_merge!(other) ⇒ Object
Merges the contents of other into the current instance
NOTE: where any key is found in both merging instances the values resulting will be a concatenation of the sets of values
Signature
-
Parameters:
other(+MultiMap+,Hash) The instance from which to merge;
Exceptions
TypeErrorRaised ifotheris not aMultiMapor aHash;
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 |
# File 'lib/xqsr3/containers/multi_map.rb', line 554 def multi_merge! other case other when self.class other.each do |k, v| self.push k, v end when ::Hash other.each do |k, v| if ::Array === v self.push k, *v else self.push k, v end end else raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}" end self end |
#push(key, *values) ⇒ Object
Pushes the given key and values. If the key is already in the
map then the values will be concatenated with those already present
Signature
-
Parameters:
keyThe element key;values(*Array) The value(s) to be pushed;
Exceptions
RangeErrorraised if the value ofcountresults in a negative count for the given element;TypeErrorifcountis not anInteger;
683 684 685 686 687 688 |
# File 'lib/xqsr3/containers/multi_map.rb', line 683 def push key, *values @inner[key] = [] unless @inner.has_key? key @inner[key].push(*values) end |
#shift ⇒ Object
Removes a key-value pair from the instance and return as a two-item array
692 693 694 695 |
# File 'lib/xqsr3/containers/multi_map.rb', line 692 def shift @inner.shift end |
#store(key, *values) ⇒ Object
Causes an element with the given key and values to be stored. If an
element with the given key already exists, its values will b
replaced
702 703 704 705 |
# File 'lib/xqsr3/containers/multi_map.rb', line 702 def store key, *values @inner[key] = values end |
#strict_merge(other) ⇒ Object
Returns a new instance containing a merging of the current instance and
the other instance
NOTE: where any key is found in both merging instances the values from
other will be used
Signature
-
Parameters:
other(+MultiMap+,Hash) The instance from which to merge;
Exceptions
TypeErrorRaised ifotheris not aMultiMapor aHash;
596 597 598 599 600 601 602 603 604 |
# File 'lib/xqsr3/containers/multi_map.rb', line 596 def strict_merge other mm = self.class.new mm.strict_merge! self mm.strict_merge! other mm end |
#strict_merge!(other) ⇒ Object
Merges the contents of other into the current instance
NOTE: where any key is found in both merging instances the values from
other will be used
Signature
-
Parameters:
other(+MultiMap+,Hash) The instance from which to merge;
Exceptions
TypeErrorRaised ifotheris not aMultiMapor aHash;
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 |
# File 'lib/xqsr3/containers/multi_map.rb', line 618 def strict_merge! other case other when self.class other.each_unflattened do |k, vals| self.store k, *vals end when ::Hash other.each do |k, v| if ::Array === v self.store k, *v else self.store k, v end end else raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}" end self end |
#to_a ⇒ Object
Converts instance to an array of [key,value] pairs
708 709 710 711 |
# File 'lib/xqsr3/containers/multi_map.rb', line 708 def to_a self.flatten end |
#to_h ⇒ Object
Obtains reference to internal hash instance (which must not be modified)
714 715 716 717 |
# File 'lib/xqsr3/containers/multi_map.rb', line 714 def to_h @inner.to_h end |
#to_hash ⇒ Object
Obtains equivalent hash to instance (independent copy)
720 721 722 723 724 725 726 727 728 729 730 |
# File 'lib/xqsr3/containers/multi_map.rb', line 720 def to_hash h = {} @inner.each do |k, vals| h[k] = vals.dup end h end |
#to_s ⇒ Object
A string-form of the instance
733 734 735 736 |
# File 'lib/xqsr3/containers/multi_map.rb', line 733 def to_s @inner.to_s end |
#values ⇒ Object
An array of all values in the instance
739 740 741 742 743 744 745 746 |
# File 'lib/xqsr3/containers/multi_map.rb', line 739 def values r = [] @inner.values.each { |vals| r += vals } r end |
#values_unflattened ⇒ Object
An array of all sets of values in the instance
749 750 751 752 |
# File 'lib/xqsr3/containers/multi_map.rb', line 749 def values_unflattened @inner.values end |