module Enumerable

Public Instance Methods

*(other) click to toggle source

Multiplies this Enumerable by something. (Same behaviour as Enumerator#*)

# File lib/epitools/core_ext/enumerable.rb, line 526
def *(other)
  case other
  when Integer, String
    to_enum * other
  when Enumerable
    to_enum.cross_product(other)
  end
end
**(n) click to toggle source

Multiplies this Enumerable by itself ‘n` times.

# File lib/epitools/core_ext/enumerable.rb, line 538
def **(n)
  [self].cycle(n).reduce(:*)
end
average() click to toggle source

Average the elements

# File lib/epitools/core_ext/enumerable.rb, line 231
def average
  count = 0
  sum   = 0

  each { |e| count += 1; sum += e }

  sum / count.to_f
end
blank?() click to toggle source

‘true’ if the Enumerable has no elements

# File lib/epitools/core_ext/enumerable.rb, line 9
def blank?
  not any?
end
combination(*args, &block) click to toggle source

See: See Array#combination

# File lib/epitools/core_ext/enumerable.rb, line 366
def combination(*args, &block)
  to_a.combination(*args, &block)
end
count_by()
Alias for: counts
counts() { |x] += 1| ... } click to toggle source

Counts how many instances of each object are in the collection, returning a hash. (Also optionally takes a block.)

eg: [:a, :b, :c, :c, :c, :c].counts #=> {:a=>1, :b=>1, :c=>4}

# File lib/epitools/core_ext/enumerable.rb, line 462
def counts
  h = Hash.of_integers
  if block_given?
    each { |x| h[yield x] += 1 }
  else
    each { |x| h[x] += 1 }
  end
  h
end
Also aliased as: count_by, group_counts
cross(other)
Alias for: cross_product
cross_product(other) click to toggle source

Same behaviour as Enumerator#cross_product

# File lib/epitools/core_ext/enumerable.rb, line 545
def cross_product(other)
  to_enum.cross_product(other)
end
Also aliased as: cross
cut_between(&block)
Alias for: split_between
deep_map(max_depth=nil, current_depth=0, parent=nil, &block)
Alias for: map_recursively
deep_select(max_depth=nil, current_depth=0, parent=nil, &block)
Alias for: select_recursively
foldl(methodname=nil, &block) click to toggle source

Identical to “reduce” in ruby1.9 (or foldl in haskell.)

Example:

array.foldl{|a,b| a + b } == array[1..-1].inject(array[0]){|a,b| a + b }
# File lib/epitools/core_ext/enumerable.rb, line 323
def foldl(methodname=nil, &block)
  result = nil

  raise "Error: pass a parameter OR a block, not both!" unless !!methodname ^ block_given?

  if methodname

    each_with_index do |e,i|
      if i == 0
        result = e
        next
      end

      result = result.send(methodname, e)
    end

  else

    each_with_index do |e,i|
      if i == 0
        result = e
        next
      end

      result = block.call(result, e)
    end

  end

  result
end
group_counts()
Alias for: counts
group_neighbors_by(&block)
Alias for: group_neighbours_by
group_neighbours_by() { |a,b| ... } click to toggle source

Associative grouping; groups all elements who share something in common with each other. You supply a block which takes two elements, and have it return true if they are “neighbours” (eg: belong in the same group).

Example:

[1,2,5,6].group_neighbours_by { |a,b| b-a <= 1 } #=> [ [1,2], [5,6] ]

(Note: This is a very fast one-pass algorithm – therefore, the groups must be pre-sorted.)

# File lib/epitools/core_ext/enumerable.rb, line 416
def group_neighbours_by(&block)
  result = []
  cluster = [first]
  each_cons(2) do |a,b|
    if yield(a,b)
      cluster << b
    else
      result << cluster
      cluster = [b]
    end
  end

  result << cluster if cluster.any?

  result
end
Also aliased as: group_neighbors_by
group_to_h()
Alias for: grouped_to_h
grouped()
Alias for: groups
grouped_to_h() click to toggle source

Converts an array of 2-element key/value pairs into a Hash, grouped by key. (Like to_h, but the pairs can have duplicate keys.)

# File lib/epitools/core_ext/enumerable.rb, line 439
def grouped_to_h
  result = Hash.of_arrays
  each {|k,v| result[k] << v }
  result
end
groups() click to toggle source

group_by the elements themselves

# File lib/epitools/core_ext/enumerable.rb, line 477
def groups
  group_by(&:self)
end
Also aliased as: grouped
iter()
Alias for: to_iter
map_recursive(max_depth=nil, current_depth=0, parent=nil, &block)
Alias for: map_recursively
map_recursively(max_depth=nil, current_depth=0, parent=nil) { |obj| ... } click to toggle source

The same as “map”, except that if an element is an Array or Enumerable, map is called recursively on that element. (Hashes are ignored because of the complications of block arguments and return values.)

Example:

[ [1,2], [3,4] ].deep_map{|e| e ** 2 } #=> [ [1,4], [9,16] ]
# File lib/epitools/core_ext/enumerable.rb, line 264
def map_recursively(max_depth=nil, current_depth=0, parent=nil, &block)
  return self if max_depth and (current_depth > max_depth)

  map do |obj|
    if obj == parent # infinite loop scenario!
      yield obj
    else
      case obj
      when String, Hash
        yield obj
      when Enumerable
        obj.map_recursively(max_depth, current_depth+1, self, &block)
      else
        yield obj
      end
    end
  end
end
Also aliased as: deep_map, recursive_map, map_recursive
parallel_map(num_workers=8, &block) click to toggle source

Map elements of this Enumerable in parallel using a pool full of Threads

eg: repos.parallel_map { |repo| system “git pull #{repo}” }

# File lib/epitools/core_ext/enumerable.rb, line 190
def parallel_map(num_workers=8, &block)
  require 'thread'

  queue = Queue.new
  each { |e| queue.push e }

  Enumerator.new(queue.size) do |y|
    workers = (0...num_workers).map do
      Thread.new do
        begin
          while e = queue.pop(true)
            y << block.call(e)
          end
        rescue ThreadError
        end
      end
    end

    workers.map(&:join)
  end
end
permutation(*args, &block) click to toggle source

See: Array#permutation

# File lib/epitools/core_ext/enumerable.rb, line 359
def permutation(*args, &block)
  to_a.permutation(*args, &block)
end
powerset() { |select.with_index { |e, i| bitmask == 1 }| ... } click to toggle source

Returns the powerset of the Enumerable

Example:

[1,2].powerset #=> [[], [1], [2], [1, 2]]
# File lib/epitools/core_ext/enumerable.rb, line 376
def powerset
  return to_enum(:powerset) unless block_given?
  a = to_a
  (0...2**a.size).each do |bitmask|
    # the bit pattern of the numbers from 0..2^(elements)-1 can be used to select the elements of the set...
    yield a.select.with_index { |e, i| bitmask[i] == 1 }
  end
end
recursive_map(max_depth=nil, current_depth=0, parent=nil, &block)
Alias for: map_recursively
recursive_select(max_depth=nil, current_depth=0, parent=nil, &block)
Alias for: select_recursively
reverse() click to toggle source
# File lib/epitools/core_ext/enumerable.rb, line 46
def reverse
  to_a.reverse
end
reverse_each() click to toggle source
# File lib/epitools/core_ext/enumerable.rb, line 55
def reverse_each
  to_a.reverse_each
end
rle() { |count, last| ... } click to toggle source

run-length encode the array (returns an array of [count, element] pairs)

# File lib/epitools/core_ext/enumerable.rb, line 485
def rle
  return to_enum(:rle) unless block_given?

  last   = nil
  result = []
  count  = 1

  each do |e|
    if last
      if last != e
        yield [count, last]
        count = 1
      else
        count += 1
      end
    end

    last = e
  end

  yield [count, last]
end
rzip(other) click to toggle source

Reverse zip (aligns the ends of two arrays, and zips them from right to left)

eg:

>> [5,39].rzip([:hours, :mins, :secs])
=> [ [:mins, 5], [:secs, 39] ]

Note: Like zip, it will pad the second array if it’s shorter than the first

# File lib/epitools/core_ext/enumerable.rb, line 394
def rzip(other)
  reverse_each.zip(other.reverse_each).reverse_each
end
select_recursive(max_depth=nil, current_depth=0, parent=nil, &block)
Alias for: select_recursively
select_recursively(max_depth=nil, current_depth=0, parent=nil) { |obj| ... } click to toggle source

The same as “select”, except that if an element is an Array or Enumerable, select is called recursively on that element.

Example:

[ [1,2], [3,4] ].select_recursively{|e| e % 2 == 0 } #=> [ [2], [4] ]
# File lib/epitools/core_ext/enumerable.rb, line 294
def select_recursively(max_depth=nil, current_depth=0, parent=nil, &block)
  return self if max_depth and (current_depth > max_depth)

  map do |obj|
    if obj == parent # infinite loop scenario!
      obj if yield obj
    else
      case obj
      when String, Hash
        obj if yield obj
      when Enumerable
        obj.deep_select(max_depth, current_depth+1, self, &block)
      else
        obj if yield obj
      end
    end
  end.compact
end
skip(n) { |x| ... } click to toggle source

Skip the first n elements and return an Enumerator for the rest, or pass them in succession to the block, if given. This is like “drop”, but returns an enumerator instead of converting the whole thing to an array.

# File lib/epitools/core_ext/enumerable.rb, line 28
def skip(n)
  if block_given?
    each do |x|
      if n > 0
        n -= 1
      else
        yield x
      end
    end
  else
    to_enum(:skip, n)
  end
end
sort_numerically() click to toggle source

Sort strings by their numerical values

# File lib/epitools/core_ext/enumerable.rb, line 511
def sort_numerically
  sort_by do |e|
    e = e.path if e.is_a? Path

    if e.is_a? String
      e.split(/(\d+)/).map { |s| s =~ /^\d+$/ ? s.to_i : s }
    else
      [e]
    end
  end
end
split_after(matcher=nil, options={}, &block) click to toggle source

Split the array into chunks, cutting between the matched element and the next element.

Example:

[1,2,3,4].split_after{|e| e == 3 } #=> [ [1,2,3], [4] ]
# File lib/epitools/core_ext/enumerable.rb, line 140
def split_after(matcher=nil, options={}, &block)
  options[:after]             ||= true
  options[:include_boundary]  ||= true
  split_at(matcher, options, &block)
end
split_at(matcher=nil, options={}, &block) click to toggle source

Split this enumerable into chunks, given some boundary condition. (Returns an array of arrays.)

Options:

:include_boundary => true  #=> include the element that you're splitting at in the results
                               (default: false)
:after => true             #=> split after the matched element (only has an effect when used with :include_boundary)
                               (default: false)
:once => flase             #=> only perform one split (default: false)

Examples:

[1,2,3,4,5].split{ |e| e == 3 }
#=> [ [1,2], [4,5] ]

"hello\n\nthere\n".each_line.split_at("\n").to_a
#=> [ ["hello\n"], ["there\n"] ]

[1,2,3,4,5].split(:include_boundary=>true) { |e| e == 3 }
#=> [ [1,2], [3,4,5] ]

chapters = File.read("ebook.txt").split(/Chapter \d+/, :include_boundary=>true)
#=> [ ["Chapter 1", ...], ["Chapter 2", ...], etc. ]
# File lib/epitools/core_ext/enumerable.rb, line 83
def split_at(matcher=nil, options={}, &block)
  include_boundary = options[:include_boundary] || false

  if matcher.nil?
    boundary_test_proc = block
  else
    if matcher.is_a? Regexp
      boundary_test_proc = proc { |element| element =~ matcher }
    else
      boundary_test_proc = proc { |element| element == matcher }
    end
  end

  Enumerator.new do |yielder|
    current_chunk = []
    splits        = 0
    max_splits    = options[:once] == true ? 1 : options[:max_splits]

    each do |e|

      if boundary_test_proc.call(e) and (max_splits == nil or splits < max_splits)

        if current_chunk.empty? and not include_boundary
          next # hit 2 boundaries in a row... just keep moving, people!
        end

        if options[:after]
          # split after boundary
          current_chunk << e        if include_boundary   # include the boundary, if necessary
          yielder << current_chunk                         # shift everything after the boundary into the resultset
          current_chunk = []                              # start a new result
        else
          # split before boundary
          yielder << current_chunk                         # shift before the boundary into the resultset
          current_chunk = []                              # start a new result
          current_chunk << e        if include_boundary   # include the boundary, if necessary
        end

        splits += 1

      else
        current_chunk << e
      end

    end

    yielder << current_chunk if current_chunk.any?

  end
end
split_before(matcher=nil, options={}, &block) click to toggle source

Split the array into chunks, cutting before each matched element.

Example:

[1,2,3,4].split_before{|e| e == 3 } #=> [ [1,2], [3,4] ]
# File lib/epitools/core_ext/enumerable.rb, line 152
def split_before(matcher=nil, options={}, &block)
  options[:include_boundary]  ||= true
  split_at(matcher, options, &block)
end
split_between() { |a,b| ... } click to toggle source

Split the array into chunks, cutting between two elements.

Example:

[1,1,2,2].split_between{|a,b| a != b } #=> [ [1,1], [2,2] ]
# File lib/epitools/core_ext/enumerable.rb, line 163
def split_between(&block)
  Enumerator.new do |yielder|
    current = []
    last    = nil

    each_cons(2) do |a,b|
      current << a
      if yield(a,b)
        yielder << current
        current = []
      end
      last = b
    end

    current << last unless last.nil?
    yielder << current
  end
end
Also aliased as: cut_between
sum(&block) click to toggle source

Sum the elements

# File lib/epitools/core_ext/enumerable.rb, line 217
def sum(&block)
  if block_given?
    map(&block).reduce(:+)
  else
    reduce(:+)
  end
end
Also aliased as: sum_by
sum_by(&block)
Alias for: sum
to_h_grouped()
Alias for: grouped_to_h
to_h_in_groups()
Alias for: grouped_to_h
to_iter() click to toggle source

Convert the array into a stable iterator (Iter) object.

# File lib/epitools/core_ext/enumerable.rb, line 451
def to_iter
  Iter.new(to_a)
end
Also aliased as: iter
uniq() { |value| ... } click to toggle source

Lazily enumerate unique elements (WARNING: This can cause an infinite loop if you enumerate over a cycle,

since it will keep reading the input until it finds a unique element)
# File lib/epitools/core_ext/enumerable.rb, line 245
def uniq
  already_seen = Set.new

  Enumerator::Lazy.new(self) do |yielder, value|
    key = block_given? ? yield(value) : value
    yielder << value if already_seen.add?(key)
  end
end
Also aliased as: uniq_by
uniq_by()
Alias for: uniq
unzip() click to toggle source

Does the opposite of zip – converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]

# File lib/epitools/core_ext/enumerable.rb, line 401
def unzip
  # TODO: make it work for arrays containing uneven-length contents
  to_a.transpose
end