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

instance

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.



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

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>>

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.



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

def bisect(ary)
  require_array!(ary)

  raise ArgumentError, 'no block given' unless block_given?

  selected = []
  rejected = []

  ary.each do |item|
    (yield(item) ? selected : rejected) << item
  end

  [selected, rejected]
end

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

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.



133
134
135
136
137
138
139
140
141
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 133

def count_values(ary, &block)
  require_array!(ary)

  ary.each.with_object({}) do |item, hsh|
    value = block_given? ? block.call(item) : item

    hsh[value] = hsh.fetch(value, 0) + 1
  end
end

#deep_dup(ary) ⇒ Array

Creates a deep copy of the object.

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:



171
172
173
174
175
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 171

def deep_dup(ary)
  require_array!(ary)

  ary.map { |obj| ObjectTools.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:



195
196
197
198
199
200
201
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 195

def deep_freeze(ary)
  require_array!(ary)

  ary.freeze

  ary.each { |obj| ObjectTools.deep_freeze obj }
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.



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

def humanize_list(ary, **options, &)
  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:, **options)

  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:



293
294
295
296
297
298
299
300
301
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 293

def immutable?(ary)
  require_array!(ary)

  return false unless ary.frozen?

  ary.each { |item| return false unless ObjectTools.immutable?(item) }

  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:



313
314
315
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 313

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

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

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.



350
351
352
353
354
355
356
357
358
359
360
# File 'lib/sleeping_king_studios/tools/array_tools.rb', line 350

def splice(ary, start, delete_count, *insert)
  require_array!(ary)

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

  ary[range] = insert

  deleted
end