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
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 132 |
# File 'lib/xqsr3/containers/multi_map.rb', line 63 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)
-
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/xqsr3/containers/multi_map.rb', line 175 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
144 145 146 147 |
# File 'lib/xqsr3/containers/multi_map.rb', line 144 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
-
159 160 161 162 163 164 165 166 |
# File 'lib/xqsr3/containers/multi_map.rb', line 159 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
193 194 195 196 |
# File 'lib/xqsr3/containers/multi_map.rb', line 193 def assoc key @inner.assoc key end |
#clear ⇒ Object
Removes all elements from the instance
199 200 201 202 |
# File 'lib/xqsr3/containers/multi_map.rb', line 199 def clear @inner.clear end |
#count ⇒ Object
The total number of instances recorded
205 206 207 208 |
# File 'lib/xqsr3/containers/multi_map.rb', line 205 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
-
214 215 216 217 |
# File 'lib/xqsr3/containers/multi_map.rb', line 214 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
-
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/xqsr3/containers/multi_map.rb', line 226 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
246 247 248 249 250 251 |
# File 'lib/xqsr3/containers/multi_map.rb', line 246 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
255 256 257 258 259 260 |
# File 'lib/xqsr3/containers/multi_map.rb', line 255 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
265 266 267 268 269 270 |
# File 'lib/xqsr3/containers/multi_map.rb', line 265 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
274 275 276 277 278 279 280 281 282 |
# File 'lib/xqsr3/containers/multi_map.rb', line 274 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
-
289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/xqsr3/containers/multi_map.rb', line 289 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
303 304 305 306 |
# File 'lib/xqsr3/containers/multi_map.rb', line 303 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
310 311 312 313 314 315 316 317 318 |
# File 'lib/xqsr3/containers/multi_map.rb', line 310 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
-
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 370 |
# File 'lib/xqsr3/containers/multi_map.rb', line 325 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
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/xqsr3/containers/multi_map.rb', line 373 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
396 397 398 399 |
# File 'lib/xqsr3/containers/multi_map.rb', line 396 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
-
405 406 407 408 409 410 411 412 413 |
# File 'lib/xqsr3/containers/multi_map.rb', line 405 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
-
422 423 424 425 426 427 |
# File 'lib/xqsr3/containers/multi_map.rb', line 422 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
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 468 |
# File 'lib/xqsr3/containers/multi_map.rb', line 438 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
471 472 473 474 |
# File 'lib/xqsr3/containers/multi_map.rb', line 471 def length @inner.size end |
#merge(other) ⇒ Object
See #merge
584 585 586 587 588 589 590 591 592 593 |
# File 'lib/xqsr3/containers/multi_map.rb', line 584 def merge other if @merge_is_multi multi_merge other else strict_merge other end end |
#merge!(other) ⇒ Object
See #merge!
596 597 598 599 600 601 602 603 604 605 |
# File 'lib/xqsr3/containers/multi_map.rb', line 596 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
-
487 488 489 490 491 492 493 494 495 |
# File 'lib/xqsr3/containers/multi_map.rb', line 487 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
-
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
# File 'lib/xqsr3/containers/multi_map.rb', line 507 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+
619 620 621 622 623 624 |
# File 'lib/xqsr3/containers/multi_map.rb', line 619 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
628 629 630 631 |
# File 'lib/xqsr3/containers/multi_map.rb', line 628 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
638 639 640 641 |
# File 'lib/xqsr3/containers/multi_map.rb', line 638 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
-
540 541 542 543 544 545 546 547 548 |
# File 'lib/xqsr3/containers/multi_map.rb', line 540 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
-
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 560 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
644 645 646 647 |
# File 'lib/xqsr3/containers/multi_map.rb', line 644 def to_a self.flatten end |
#to_h ⇒ Object
Obtains reference to internal hash instance (which must not be modified)
650 651 652 653 |
# File 'lib/xqsr3/containers/multi_map.rb', line 650 def to_h @inner.to_h end |
#to_hash ⇒ Object
Obtains equivalent hash to instance
656 657 658 659 |
# File 'lib/xqsr3/containers/multi_map.rb', line 656 def to_hash @elements.to_hash end |
#to_s ⇒ Object
A string-form of the instance
662 663 664 665 |
# File 'lib/xqsr3/containers/multi_map.rb', line 662 def to_s @inner.to_s end |
#values ⇒ Object
An array of all values in the instance
668 669 670 671 672 673 674 675 |
# File 'lib/xqsr3/containers/multi_map.rb', line 668 def values r = [] @inner.values.each { |vals| r += vals } r end |
#values_unflattened ⇒ Object
An array of all sets of values in the instance
678 679 680 681 |
# File 'lib/xqsr3/containers/multi_map.rb', line 678 def values_unflattened @inner.values end |