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
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 131 |
# File 'lib/xqsr3/containers/multi_map.rb', line 62 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
-
Parameters:
-
rhs
(nil
,::Hash
,MultiMap
) The instance to compare against
-
-
Exceptions:
-
::TypeError
ifrhs
is not of the required type(s)
-
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/xqsr3/containers/multi_map.rb', line 174 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
143 144 145 146 |
# File 'lib/xqsr3/containers/multi_map.rb', line 143 def [] key return @inner[key] end |
#[]=(key, values) ⇒ Object
Adds/assigns a new key+values pair. Equivalent to
store(key, *values)
-
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
192 193 194 195 |
# File 'lib/xqsr3/containers/multi_map.rb', line 192 def assoc key @inner.assoc key end |
#clear ⇒ Object
Removes all elements from the instance
198 199 200 201 |
# File 'lib/xqsr3/containers/multi_map.rb', line 198 def clear @inner.clear end |
#count ⇒ Object
The total number of instances recorded
204 205 206 207 |
# File 'lib/xqsr3/containers/multi_map.rb', line 204 def count @inner.each_value.map { |ar| ar.size}.inject(0, :+) end |
#delete(key) ⇒ Object
Deletes all values mapped with the given key
-
Parameters:
-
key
The key to delete
-
213 214 215 216 |
# File 'lib/xqsr3/containers/multi_map.rb', line 213 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
-
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/xqsr3/containers/multi_map.rb', line 225 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
245 246 247 248 249 250 |
# File 'lib/xqsr3/containers/multi_map.rb', line 245 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
254 255 256 257 258 259 |
# File 'lib/xqsr3/containers/multi_map.rb', line 254 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
264 265 266 267 268 269 |
# File 'lib/xqsr3/containers/multi_map.rb', line 264 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
273 274 275 276 277 278 279 280 281 |
# File 'lib/xqsr3/containers/multi_map.rb', line 273 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
-
288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/xqsr3/containers/multi_map.rb', line 288 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
302 303 304 305 |
# File 'lib/xqsr3/containers/multi_map.rb', line 302 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
309 310 311 312 313 314 315 316 317 |
# File 'lib/xqsr3/containers/multi_map.rb', line 309 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
-
Parameters:
-
key
The key -
default
The default value
-
324 325 326 327 328 329 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 |
# File 'lib/xqsr3/containers/multi_map.rb', line 324 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
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/xqsr3/containers/multi_map.rb', line 372 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
395 396 397 398 |
# File 'lib/xqsr3/containers/multi_map.rb', line 395 def has_key? key @inner.has_key? key end |
#has_value?(value) ⇒ Boolean
Returns true
if any key has the given value
; false
otherwise
-
Parameters:
-
value
The value for which to search
-
404 405 406 407 408 409 410 411 412 |
# File 'lib/xqsr3/containers/multi_map.rb', line 404 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
-
Parameters:
-
values
(Array) The values for which to search
-
-
Exceptions:
-
::TypeError
ifvalue
is not an Array
-
421 422 423 424 425 426 |
# File 'lib/xqsr3/containers/multi_map.rb', line 421 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)
-
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
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/xqsr3/containers/multi_map.rb', line 437 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
470 471 472 473 |
# File 'lib/xqsr3/containers/multi_map.rb', line 470 def length @inner.size end |
#merge(other) ⇒ Object
See #merge
583 584 585 586 587 588 589 590 591 592 |
# File 'lib/xqsr3/containers/multi_map.rb', line 583 def merge other if @merge_is_multi multi_merge other else strict_merge other end end |
#merge!(other) ⇒ Object
See #merge!
595 596 597 598 599 600 601 602 603 604 |
# File 'lib/xqsr3/containers/multi_map.rb', line 595 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
-
Parameters:
-
other
(MultiMap, Hash) The instance from which to merge
-
-
Exceptions:
-
TypeError
Raised ifother
is not a MultiMap or a Hash
-
486 487 488 489 490 491 492 493 494 |
# File 'lib/xqsr3/containers/multi_map.rb', line 486 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
-
Parameters:
-
other
(MultiMap, Hash) The instance from which to merge
-
-
Exceptions:
-
TypeError
Raised ifother
is not a MultiMap or a Hash
-
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/xqsr3/containers/multi_map.rb', line 506 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 of +count+ results in a negative count for the given element
- +::TypeError+ if +count+ is not an +::Integer+
618 619 620 621 622 623 |
# File 'lib/xqsr3/containers/multi_map.rb', line 618 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
627 628 629 630 |
# File 'lib/xqsr3/containers/multi_map.rb', line 627 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
637 638 639 640 |
# File 'lib/xqsr3/containers/multi_map.rb', line 637 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
-
Parameters:
-
other
(MultiMap, Hash) The instance from which to merge
-
-
Exceptions:
-
TypeError
Raised ifother
is not a MultiMap or a Hash
-
539 540 541 542 543 544 545 546 547 |
# File 'lib/xqsr3/containers/multi_map.rb', line 539 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
-
Parameters:
-
other
(MultiMap, Hash) The instance from which to merge
-
-
Exceptions:
-
TypeError
Raised ifother
is not a MultiMap or a Hash
-
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
# File 'lib/xqsr3/containers/multi_map.rb', line 559 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
643 644 645 646 |
# File 'lib/xqsr3/containers/multi_map.rb', line 643 def to_a self.flatten end |
#to_h ⇒ Object
Obtains reference to internal hash instance (which must not be modified)
649 650 651 652 |
# File 'lib/xqsr3/containers/multi_map.rb', line 649 def to_h @inner.to_h end |
#to_hash ⇒ Object
Obtains equivalent hash to instance
655 656 657 658 |
# File 'lib/xqsr3/containers/multi_map.rb', line 655 def to_hash @elements.to_hash end |
#to_s ⇒ Object
A string-form of the instance
661 662 663 664 |
# File 'lib/xqsr3/containers/multi_map.rb', line 661 def to_s @inner.to_s end |
#values ⇒ Object
An array of all values in the instance
667 668 669 670 671 672 673 674 |
# File 'lib/xqsr3/containers/multi_map.rb', line 667 def values r = [] @inner.values.each { |vals| r += vals } r end |
#values_unflattened ⇒ Object
An array of all sets of values in the instance
677 678 679 680 |
# File 'lib/xqsr3/containers/multi_map.rb', line 677 def values_unflattened @inner.values end |