Module: Tins::EnumSqueeze

Defined in:
lib/tins/enum_squeeze.rb

Constant Summary collapse

A_THING_THAT_IS_NOT_A_THING =
Object.new.freeze

Instance Method Summary collapse

Instance Method Details

#squeeze(*sels, &block) ⇒ Array

Collapse consecutive duplicate elements, mirroring String#squeeze.

Two elements are considered duplicates when item == previous is true.

Optionally restrict squeezing to elements matching one or more selectors via case-equality (+===+), mirroring how String#squeeze uses character-class expressions. Elements not matching any selector are never collapsed.

Examples:

Basic usage:

[1, 1, 2, 2, 3, 1].squeeze  # => [1, 2, 3, 1]

Only consecutive duplicates are collapsed:

[1, 2, 1, 2, 1].squeeze     # => [1, 2, 1, 2, 1]

Mirrors String#squeeze semantics:

"aabbcc".squeeze            # => "abc"

Squeeze only elements in a range:

[1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(2..3)
# => [1, 3, 2, 4, 6, 3, 7]

Squeeze only elements of a given type:

[[1], '1', [1], [1], '1', '1', [1]].squeeze(String)
# => [[1], "1", [1], [1], "1", [1]]

Squeeze using a predicate block:

[1, 3, 2, 2, 4, 6, 3, 3, 7].squeeze(&:even?)
# => [1, 3, 2, 4, 6, 3, 3, 7]

Enumerable/String invariance:

str = 'fooaabaaz'
str.squeeze(?a) == str.split('').squeeze(?a).join
# => true

Parameters:

  • sels (Array) —

    Case-equality matchers (types, ranges, regexes, or any object responding to ===) defining which elements are eligible for squeezing.

  • block (Proc, nil) —

    A predicate; when it returns false for an element, that element is never collapsed. Mutually exclusive with sels.

Returns:

  • (Array) —

    a new array with consecutive duplicates removed



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/tins/enum_squeeze.rb', line 53

def squeeze(*sels, &block)
  !sels.empty? && block and raise ArgumentError,
    'you cannot pass both *sels and &block'

  unless block
    if sels.empty?
      block = -> x { true }
    else
      block = -> x { sels.any? { _1 === x } }
    end
  end

  result = []
  prev   = A_THING_THAT_IS_NOT_A_THING

  each do |item|
    if item != prev || !block.(item)
      result << item
      prev = item
    end
  end

  result
end

#squeeze!(*sels, &block) ⇒ self?

Collapse consecutive duplicate elements in place.

Destructive counterpart to squeeze: the receiver is modified via replace; no new object is allocated.

Examples:

Squeezing an array with consecutive duplicates:

a = [1, 1, 2, 2, 3, 1]
a.squeeze!  # => self
a           # => [1, 2, 3, 1]

No consecutive duplicates — returns nil:

b = [1, 2, 3]
b.squeeze!  # => nil
b           # => [1, 2, 3]

Squeezing with a selector:

a = [1, 3, 2, 2, 4, 6, 3, 3, 7]
a.squeeze!(2..3)
a           # => [1, 3, 2, 4, 6, 3, 7]

Parameters:

  • sels (Array) —

    Case-equality matchers (see squeeze).

  • block (Proc, nil) —

    A predicate (see squeeze).

Returns:

  • (self) —

    if one or more consecutive duplicates were removed

  • (nil) —

    if the receiver was already free of consecutive duplicates

Raises:

  • (RuntimeError) —

    if the receiver does not respond to replace and therefore cannot be squeezed in place

See Also:



109
110
111
112
113
114
# File 'lib/tins/enum_squeeze.rb', line 109

def squeeze!(*sels, &block)
  respond_to?(:replace) or raise 'cannot be squeezed in place!'
  squeezed = squeeze(*sels, &block)
  return if squeezed.count == count
  replace squeezed
end