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
nil
if 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, ornil
if 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
true
if instance contains no elements;false
otherwise. -
#eql?(rhs) ⇒ Boolean
Returns
true
ifrhs
is an instance ofMultiMap
and contains the same elements and their counts;false
otherwise. -
#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
true
if an element with the givenkey
is in the map;false
otherwise. -
#has_value?(value) ⇒ Boolean
Returns
true
if any key has the givenvalue
;false
otherwise. -
#has_values?(values) ⇒ Boolean
Returns
true
if any key has the givenvalues
;false
otherwise. -
#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
other
instance. -
#multi_merge!(other) ⇒ Object
Merges the contents of
other
into the current instance. -
#push(key, *values) ⇒ Object
Pushes the given
key
andvalues
. -
#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
key
andvalues
to be stored. -
#strict_merge(other) ⇒ Object
Returns a new instance containing a merging of the current instance and the
other
instance. -
#strict_merge!(other) ⇒ Object
Merges the contents of
other
into 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
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
-
TypeError
ifrhs
is 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:
-
key
The element key; -
values
(Array
) The values to be associated with the key;
-
Exceptions
-
TypeError
ifvalues
is 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:
-
key
The 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
-
ArgumentError
if more than 1defaults
is 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
-
ArgumentError
if 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 322 323 |
# 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:
-
key
The key -
default
The default value
-
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 376 377 378 379 380 381 382 383 384 |
# File 'lib/xqsr3/containers/multi_map.rb', line 332 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
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/xqsr3/containers/multi_map.rb', line 387 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
410 411 412 413 |
# File 'lib/xqsr3/containers/multi_map.rb', line 410 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:
-
value
The value for which to search
-
421 422 423 424 425 426 427 428 429 |
# File 'lib/xqsr3/containers/multi_map.rb', line 421 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
-
TypeError
ifvalue
is not an Array;
440 441 442 443 444 445 |
# File 'lib/xqsr3/containers/multi_map.rb', line 440 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
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/xqsr3/containers/multi_map.rb', line 458 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
491 492 493 494 |
# File 'lib/xqsr3/containers/multi_map.rb', line 491 def length @inner.size end |
#merge(other) ⇒ Object
See #merge
612 613 614 615 616 617 618 619 620 621 |
# File 'lib/xqsr3/containers/multi_map.rb', line 612 def merge other if @merge_is_multi multi_merge other else strict_merge other end end |
#merge!(other) ⇒ Object
See #merge!
624 625 626 627 628 629 630 631 632 633 |
# File 'lib/xqsr3/containers/multi_map.rb', line 624 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
-
TypeError
Raised ifother
is not aMultiMap
or aHash
;
509 510 511 512 513 514 515 516 517 |
# File 'lib/xqsr3/containers/multi_map.rb', line 509 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
-
TypeError
Raised ifother
is not aMultiMap
or aHash
;
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/xqsr3/containers/multi_map.rb', line 531 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:
-
key
The element key; -
values
(*Array) The value(s) to be pushed;
-
Exceptions
-
RangeError
raised if the value ofcount
results in a negative count for the given element; -
TypeError
ifcount
is not anInteger
;
647 648 649 650 651 652 |
# File 'lib/xqsr3/containers/multi_map.rb', line 647 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
656 657 658 659 |
# File 'lib/xqsr3/containers/multi_map.rb', line 656 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
666 667 668 669 |
# File 'lib/xqsr3/containers/multi_map.rb', line 666 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
-
TypeError
Raised ifother
is not aMultiMap
or aHash
;
566 567 568 569 570 571 572 573 574 |
# File 'lib/xqsr3/containers/multi_map.rb', line 566 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
-
TypeError
Raised ifother
is not aMultiMap
or aHash
;
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
# File 'lib/xqsr3/containers/multi_map.rb', line 588 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
672 673 674 675 |
# File 'lib/xqsr3/containers/multi_map.rb', line 672 def to_a self.flatten end |
#to_h ⇒ Object
Obtains reference to internal hash instance (which must not be modified)
678 679 680 681 |
# File 'lib/xqsr3/containers/multi_map.rb', line 678 def to_h @inner.to_h end |
#to_hash ⇒ Object
Obtains equivalent hash to instance
684 685 686 687 |
# File 'lib/xqsr3/containers/multi_map.rb', line 684 def to_hash @elements.to_hash end |
#to_s ⇒ Object
A string-form of the instance
690 691 692 693 |
# File 'lib/xqsr3/containers/multi_map.rb', line 690 def to_s @inner.to_s end |
#values ⇒ Object
An array of all values in the instance
696 697 698 699 700 701 702 703 |
# File 'lib/xqsr3/containers/multi_map.rb', line 696 def values r = [] @inner.values.each { |vals| r += vals } r end |
#values_unflattened ⇒ Object
An array of all sets of values in the instance
706 707 708 709 |
# File 'lib/xqsr3/containers/multi_map.rb', line 706 def values_unflattened @inner.values end |