Class: SleepingKingStudios::Tools::ArrayTools

Inherits:
Base
  • Object
show all
Defined in:
lib/sleeping_king_studios/tools/array_tools.rb

Overview

Tools for working with array-like enumerable objects.

Constant Summary collapse

ARRAY_METHODS =

Expected methods that an Array-like object should implement.

%i[[] count each].freeze
OTHER_METHODS =

Methods that an Array-like object should not implement.

%i[each_key each_pair].freeze

Instance Method Summary collapse

Methods inherited from Base

#initialize, instance, #toolbelt

Constructor Details

This class inherits a constructor from SleepingKingStudios::Tools::Base

Instance Method Details

#array?(obj) ⇒ Boolean

Returns true if the object is or appears to be an Array.

This method checks for the method signatures of the object. An Array-like method will define all of the the #[], #count, and #each methods, and neither of the #each_key or #each_pair methods.

Examples:

ArrayTools.array?(nil)
#=> false

ArrayTools.array?([])
#=> true

ArrayTools.array?({})
#=> false

Parameters:

  • obj (Object)

    the object to test.

Returns:

  • (Boolean)

    true if the object is an Array, otherwise false.



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 48

def array?(obj)
  return true if obj.is_a?(Array)

  ARRAY_METHODS.each do |method_name|
    return false unless obj.respond_to?(method_name)
  end

  OTHER_METHODS.each do |method_name|
    return false if obj.respond_to?(method_name)
  end

  true
end

#bisect(ary) {|item| ... } ⇒ Array<Array<Object>>

Deprecated.

v1.3.0 Use Enumerable#partition instead.

Partitions the array into matching and non-matching items.

Separates the array into two arrays, the first containing all items in the original array that matches the provided block, and the second containing all items in the original array that do not match the provided block.

Examples:

selected, rejected = ArrayTools.bisect([*0...10]) { |item| item.even? }
selected
#=> [0, 2, 4, 6, 8]
rejected
#=> [1, 3, 5, 7, 9]

Parameters:

  • ary (Array<Object>)

    the array to bisect.

Yield Parameters:

  • item (Object)

    an item in the array to matched.

Yield Returns:

  • (Boolean)

    true if the item matches the criteria, otherwise false.

Returns:

  • (Array<Array<Object>>)

    an array containing two arrays.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object, or if no block is given.



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 88

def bisect(ary, &)
  toolbelt.core_tools.deprecate(
    "#{self.class.name}#bisect",
    message: 'Use Enumerable#partition instead.'
  )

  require_array!(ary)

  raise ArgumentError, 'no block given' unless block_given?

  ary.each.partition(&)
end

#count_values(ary) ⇒ Hash{Object=>Integer} #count_values(ary) {|item| ... } ⇒ Hash{Object=>Integer} Also known as: tally

Deprecated.

v1.3.0 Use Enumerable#tally instead.

Counts the number of times each item or result appears in the object.

Overloads:

  • #count_values(ary) ⇒ Hash{Object=>Integer}

    Counts the number of times each value appears in the enumerable object.

    Examples:

    ArrayTools.count_values([1, 1, 1, 2, 2, 3])
    #=> { 1 => 3, 2 => 2, 3 => 1 }

    Parameters:

    • ary (Array<Object>)

      the values to count.

    Returns:

    • (Hash{Object=>Integer})

      The number of times each value appears in the enumerable object.

    Raises:

    • (ArgumentError)

      if the first argument is not an Array-like object.

  • #count_values(ary) {|item| ... } ⇒ Hash{Object=>Integer}

    Calls the block and counts the number of times each result appears.

    Examples:

    ArrayTools.count_values([1, 1, 1, 2, 2, 3]) { |i| i ** 2 }
    #=> { 1 => 3, 4 => 2, 9 => 1 }

    Parameters:

    • ary (Array<Object>)

      the values to count.

    Yield Parameters:

    • item (Object)

      an item in the array to matched.

    Returns:

    • (Hash{Object=>Integer})

      the number of times each result appears.

    Raises:

    • (ArgumentError)

      if the first argument is not an Array-like object.



136
137
138
139
140
141
142
143
144
145
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 136

def count_values(ary, &)
  toolbelt.core_tools.deprecate(
    "#{self.class.name}#count_values",
    message: 'Use Enumerable#tally instead.'
  )

  require_array!(ary)

  (block_given? ? ary.map(&) : ary.to_a).tally
end

#deep_dup(ary) ⇒ Array

Creates a deep copy of the array and its contents.

Iterates over the array and returns a new Array with deep copies of each array item.

Examples:

ary = ['one', 'two', 'three']
cpy = ArrayTools.deep_dup ary

cpy << 'four'
#=> ['one', 'two', 'three', 'four']
ary
#=> ['one', 'two', 'three']

cpy.first.sub!(/on/, 'vu')
cpy
#=> ['vun', 'two', 'three', 'four']
ary
#=> ['one', 'two', 'three']

Parameters:

  • ary (Array<Object>)

    the array to copy.

Returns:

  • (Array)

    the copy of the array.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object.

See Also:



175
176
177
178
179
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 175

def deep_dup(ary)
  require_array!(ary)

  ary.map { |obj| toolbelt.object_tools.deep_dup obj }
end

#deep_freeze(ary) ⇒ Array

Freezes the array and performs a deep freeze on each array item.

Examples:

ary = ['one', 'two', 'three']
ArrayTools.deep_freeze ary

ary.frozen?
#=> true
ary.first.frozen?
#=> true

Parameters:

  • ary (Array)

    the object to freeze.

Returns:

  • (Array)

    the frozen array.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object.

See Also:



199
200
201
202
203
204
205
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 199

def deep_freeze(ary)
  require_array!(ary)

  ary.freeze

  ary.each { |obj| toolbelt.object_tools.deep_freeze obj }
end

#fetch(ary, index, default = nil) ⇒ Object #fetch(ary, index) {|index| ... } ⇒ Object

Overloads:

  • #fetch(ary, index, default = nil) ⇒ Object

    Retrieves the value at the specified index.

    If the value does not exist, returns the default value, or raises an IndexError if there is no default value. If the object defines a native #fetch method, delegates to the native implementation.

    Parameters:

    • ary (Array)

      the array or array-like object.

    • index (Integer)

      the index to retrieve.

    • default (Object) (defaults to: nil)

      the default value.

    Returns:

    • (Object)

      the value at the specified index.

    Raises:

    • (IndexError)

      if the array does not have a value at that index and there is no default value.

  • #fetch(ary, index) {|index| ... } ⇒ Object

    Retrieves the value at the specified index.

    If the value does not exist, returns the value of the default block, or raises an IndexError if there is no default block. If the object defines a native #fetch method, delegates to the native implementation.

    Parameters:

    • ary (Array)

      the array or array-like object.

    • index (Integer)

      the index to retrieve.

    Yields:

    • generates the default value if there is no value at the index.

    Yield Parameters:

    • index (Integer)

      the requested index.

    Yield Returns:

    • (Object)

      the default value.

    Returns:

    • (Object)

      the value at the specified index.

    Raises:

    • (IndexError)

      if the array does not have a value at that index and there is no default value.

Raises:

  • (IndexError)


243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 243

def fetch(ary, index, default = UNDEFINED, &block)
  require_array!(ary)

  if ary.respond_to?(:fetch)
    return native_fetch(ary, index, default, &block)
  end

  size = ary.respond_to?(:size) ? ary.size : ary.count

  return ary[index] if index < size && index >= -size

  return block.call(index) if block_given?

  return default unless default == UNDEFINED

  raise IndexError,
    "index #{index} outside of array bounds: -#{size}...#{size}"
end

#humanize_list(ary, **options, &) ⇒ String

Generates a human-readable string representation of the list items.

Accepts a list of values and returns a human-readable string of the values, with the format based on the number of items.

Examples:

With Zero Items

ArrayTools.humanize_list([])
#=> ''

With One Item

ArrayTools.humanize_list(['spam'])
#=> 'spam'

With Two Items

ArrayTools.humanize_list(['spam', 'eggs'])
#=> 'spam and eggs'

With Three Or More Items

ArrayTools.humanize_list(['spam', 'eggs', 'bacon', 'spam'])
#=> 'spam, eggs, bacon, and spam'

With Three Or More Items And Options

ArrayTools.humanize_list(
  ['spam', 'eggs', 'bacon', 'spam'],
  :last_separator => ' or '
)
#=> 'spam, eggs, bacon, or spam'

Parameters:

  • ary (Array<String>)

    the list of values to format. Will be coerced to strings using #to_s.

  • options (Hash)

    optional configuration hash.

Options Hash (**options):

  • :last_separator (String)

    the value to use to separate the final pair of values. Defaults to “ and ” (note the leading and trailing spaces). Will be combined with the :separator for lists of length 3 or greater.

  • :separator (String)

    the value to use to separate pairs of values before the last in lists of length 3 or greater. Defaults to “, ” (note the trailing space).

Returns:

  • (String)

    the formatted string.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object.



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 307

def humanize_list(ary, **, &)
  require_array!(ary)

  return '' if ary.empty?

  size = ary.size
  ary  = ary.map(&) if block_given?

  return ary[0].to_s if size == 1

  separator, last_separator =
    options_for_humanize_list(size:, **)

  return "#{ary[0]}#{last_separator}#{ary[1]}" if size == 2

  "#{ary[0...-1].join(separator)}#{last_separator}#{ary.last}"
end

#immutable?(ary) ⇒ Boolean

Checks if the array and its contents are immutable.

An array is considered immutable if the array itself is frozen and each item in the array is immutable.

Examples:

ArrayTools.immutable?([1, 2, 3])
#=> false

ArrayTools.immutable?([1, 2, 3].freeze)
#=> true

ArrayTools.immutable?([+'ichi', +'ni', +'san'])
#=> false

ArrayTools.immutable?([+'ichi', +'ni', +'san'].freeze)
#=> false

ArrayTools.immutable?(['ichi', 'ni', 'san'].freeze)
#=> true

Parameters:

  • ary (Array)

    the array to test.

Returns:

  • (Boolean)

    true if the array is immutable, otherwise false.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object.

See Also:



355
356
357
358
359
360
361
362
363
364
365
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 355

def immutable?(ary)
  require_array!(ary)

  return false unless ary.frozen?

  ary.each do |item|
    return false unless toolbelt.object_tools.immutable?(item)
  end

  true
end

#mutable?(ary) ⇒ Boolean

Checks if the array or any of its contents are mutable.

Parameters:

  • ary (Array)

    the array to test.

Returns:

  • (Boolean)

    true if the array or any of its items are mutable, otherwise false.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object.

See Also:



377
378
379
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 377

def mutable?(ary)
  !immutable?(ary)
end

#splice(ary, start, delete_count, *insert) ⇒ Array<Object>

Deprecated.

v1.3.0 Use Array#[]= with a range instead.

Replaces a range of items in the array with the given items.

Examples:

Deleting items from an Array

values = %w(katana wakizashi tachi daito shoto)
ArrayTools.splice values, 1, 2
#=> ['wakizashi', 'tachi']
values
#=> ['katana', 'daito', 'shoto']

Inserting items into an Array

values = %w(longsword broadsword claymore)
ArrayTools.splice values, 1, 0, 'zweihander'
#=> []
values
#=> ['longsword', 'zweihander', 'broadsword', 'claymore']

Inserting and deleting items

values = %w(shortbow longbow crossbow)
ArrayTools.splice values, 2, 1, 'arbalest', 'chu-ko-nu'
#=> ['crossbow']
values
#=> ['shortbow', 'longbow', 'arbalest', 'chu-ko-nu']

Parameters:

  • ary (Array<Object>)

    the array to splice.

  • start (Integer)

    the starting index to delete or insert values from or into. If negative, counts backward from the end of the array.

  • delete_count (Integer)

    the number of items to delete.

  • insert (Array<Object>)

    the items to insert, if any.

Returns:

  • (Array<Object>)

    the deleted items, or an empty array if no items were deleted.

Raises:

  • (ArgumentError)

    if the first argument is not an Array-like object.



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 416

def splice(ary, start, delete_count, *insert)
  toolbelt.core_tools.deprecate(
    "#{self.class.name}#splice",
    message: 'Use Array#[]= with a range instead.'
  )

  require_array!(ary)

  start  += ary.count if start.negative?
  range   = start...(start + delete_count)
  deleted = ary[range]

  ary[range] = insert

  deleted
end