Class: Xqsr3::Containers::MultiMap
- Includes:
- Enumerable
- Defined in:
- lib/xqsr3/containers/multi_map.rb
Overview
Hash-like class that stores as 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
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.
-
#key(*values) ⇒ Object
Returns the key for the given value(s).
-
#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.
-
#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
Methods inherited from Hash
#except, #except!, #has_match?, #match, #slice
Methods included from HashUtilities::DeepTransform
#deep_transform, #deep_transform!
Constructor Details
#initialize ⇒ MultiMap
Initialises an instance
132 133 134 135 136 137 |
# File 'lib/xqsr3/containers/multi_map.rb', line 132 def initialize @merge_is_multi = true @inner = Hash.new end |
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 |
# 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.shift ar.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)
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/xqsr3/containers/multi_map.rb', line 176 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
141 142 143 144 |
# File 'lib/xqsr3/containers/multi_map.rb', line 141 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
158 159 160 161 162 163 164 165 |
# File 'lib/xqsr3/containers/multi_map.rb', line 158 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
194 195 196 197 |
# File 'lib/xqsr3/containers/multi_map.rb', line 194 def assoc key @inner.assoc key end |
#clear ⇒ Object
Removes all elements from the instance
200 201 202 203 |
# File 'lib/xqsr3/containers/multi_map.rb', line 200 def clear @inner.clear end |
#count ⇒ Object
The total number of instances recorded
206 207 208 209 |
# File 'lib/xqsr3/containers/multi_map.rb', line 206 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
-
217 218 219 220 |
# File 'lib/xqsr3/containers/multi_map.rb', line 217 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
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/xqsr3/containers/multi_map.rb', line 229 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
249 250 251 252 253 254 |
# File 'lib/xqsr3/containers/multi_map.rb', line 249 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
258 259 260 261 262 263 |
# File 'lib/xqsr3/containers/multi_map.rb', line 258 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
268 269 270 271 272 273 |
# File 'lib/xqsr3/containers/multi_map.rb', line 268 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
277 278 279 280 281 282 283 284 285 |
# File 'lib/xqsr3/containers/multi_map.rb', line 277 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
292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/xqsr3/containers/multi_map.rb', line 292 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
306 307 308 309 |
# File 'lib/xqsr3/containers/multi_map.rb', line 306 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
313 314 315 316 317 318 319 320 321 |
# File 'lib/xqsr3/containers/multi_map.rb', line 313 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 key -
defaultThe default value
-
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 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 |
# File 'lib/xqsr3/containers/multi_map.rb', line 330 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
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 378 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
Returns true if an element with the given key is in the map; false otherwise
401 402 403 404 |
# File 'lib/xqsr3/containers/multi_map.rb', line 401 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
-
412 413 414 415 416 417 418 419 420 |
# File 'lib/xqsr3/containers/multi_map.rb', line 412 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;
431 432 433 434 435 436 |
# File 'lib/xqsr3/containers/multi_map.rb', line 431 def has_values? values raise TypeError, "'values' parameter must be of type #{::Array}" unless Array === values @inner.has_value? values 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
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/xqsr3/containers/multi_map.rb', line 449 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 |
#length ⇒ Object Also known as: size
The number of elements in the map
482 483 484 485 |
# File 'lib/xqsr3/containers/multi_map.rb', line 482 def length @inner.size end |
#merge(other) ⇒ Object
See #merge
603 604 605 606 607 608 609 610 611 612 |
# File 'lib/xqsr3/containers/multi_map.rb', line 603 def merge other if @merge_is_multi multi_merge other else strict_merge other end end |
#merge!(other) ⇒ Object
See #merge!
615 616 617 618 619 620 621 622 623 624 |
# File 'lib/xqsr3/containers/multi_map.rb', line 615 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;
500 501 502 503 504 505 506 507 508 |
# File 'lib/xqsr3/containers/multi_map.rb', line 500 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;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/xqsr3/containers/multi_map.rb', line 522 def multi_merge! other case other when self.class ; when ::Hash ; else raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}" end other.each do |k, v| self.push k, v 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;
638 639 640 641 642 643 |
# File 'lib/xqsr3/containers/multi_map.rb', line 638 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
647 648 649 650 |
# File 'lib/xqsr3/containers/multi_map.rb', line 647 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
657 658 659 660 |
# File 'lib/xqsr3/containers/multi_map.rb', line 657 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;
557 558 559 560 561 562 563 564 565 |
# File 'lib/xqsr3/containers/multi_map.rb', line 557 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;
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/xqsr3/containers/multi_map.rb', line 579 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| self.store k, v 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
663 664 665 666 |
# File 'lib/xqsr3/containers/multi_map.rb', line 663 def to_a self.flatten end |
#to_h ⇒ Object
Obtains reference to internal hash instance (which must not be modified)
669 670 671 672 |
# File 'lib/xqsr3/containers/multi_map.rb', line 669 def to_h @inner.to_h end |
#to_hash ⇒ Object
Obtains equivalent hash to instance
675 676 677 678 |
# File 'lib/xqsr3/containers/multi_map.rb', line 675 def to_hash @elements.to_hash end |
#to_s ⇒ Object
A string-form of the instance
681 682 683 684 |
# File 'lib/xqsr3/containers/multi_map.rb', line 681 def to_s @inner.to_s end |
#values ⇒ Object
An array of all values in the instance
687 688 689 690 691 692 693 694 |
# File 'lib/xqsr3/containers/multi_map.rb', line 687 def values r = [] @inner.values.each { |vals| r += vals } r end |
#values_unflattened ⇒ Object
An array of all sets of values in the instance
697 698 699 700 |
# File 'lib/xqsr3/containers/multi_map.rb', line 697 def values_unflattened @inner.values end |