module Enumerable

Public Class Methods

cartesian_product(*enums) click to toggle source

@return [Array] the cartesian product of the enumerations.

@see en.wikipedia.org/wiki/Cartesian_product

This is the fastest implementation we have found. It returns results in typical order.

@author Thomas Hafner @see www.ruby-forum.com/topic/95519

For our benchmarks, we also compared these:

# File lib/sixarm_ruby_ramp/enumerable.rb, line 198
def self.cartesian_product(*enums)
  result = [[]]
  while [] != enums
    t, result = result, []
    b, *enums = enums
    t.each do |a|
      b.each do |n|
        result << a + [n]
      end
    end
  end
  result
end

Public Instance Methods

bisect() { |item| ... } click to toggle source

@example

enum.bisect {|obj| block}
=> array of positives, array of negatives

@return [Array<Array<Object>] an array of two arrays:

the first array is the elements for which block is true,
the second array is the elements for which block is false or nil.
# File lib/sixarm_ruby_ramp/enumerable.rb, line 112
def bisect
  accept=[]
  reject=[]
  each{|item|
   if yield(item)
    accept << item
   else
    reject << item
   end
  }
  return accept,reject
end
cartesian_product(*enums) click to toggle source

Calculate the cartesian product.

# File lib/sixarm_ruby_ramp/enumerable.rb, line 214
def cartesian_product(*enums)
  Enumerable.cartesian_product(self,*enums)
end
each_at(filter) { |at(i)| ... } click to toggle source

Get each element at a given index or indices.

Example: use an index.

["a", "b", "c", "d", "e"].each_at(1)
#=> "b"

Example: use an index that is negative when size is known.

["a", "b", "c", "d", "e"].each_at(-1)
=> "e"

Example: use a range.

["a", "b", "c", "d", "e"].each_at(1..3)
=> "b", "c", "d"

Example: use a range that has negatives when size is known.

["a", "b", "c", "d", "e"].each_at(-3..-1)
=> "c", "d", "e"

Example: use any object that responds to each or include?.

["a", "b", "c", "d", "e"].each_at([4, 2, -2])
=> "e", "c", "d"
# File lib/sixarm_ruby_ramp/enumerable/each.rb, line 30
def each_at(filter)
  filter, optimize_for_whole_numbers = each_at_normalize_filter(filter)

  # Handle variations.
  #
  # Can we call self#at?
  #
  #  * Yes: look up a self element by index.
  #  * No: iterate on self, comparing the loop count to filter target.
  #
  # Can we call filter#each?
  #
  #  * Yes: iterate on the filter elements.
  #  * No:  iterate on self, comparing the loop count to filter#include?
  #
  # Can we optimize for whole numbers?
  #
  #  * Yes: we know that all filter targets are whole numbers.
  #  * No: we must convert the filter target to an index size each time.
  #
  if self.respond_to?(:at) && filter.respond_to?(:each)
    if optimize_for_whole_numbers
      # each_at_strategy_with_self_at_and_filter_each_and_whole_numbers(filter)
      filter.each{|i|
        yield(at(i))
      }
    else
      # each_at_strategy_with_self_at_and_filter_each(filter)
      filter.each{|i|
        yield(at(i >= 0 ? i : i + self.size))
      }
    end
  elsif filter.respond_to?(:include?)
    # each_at_strategy_with_count(filter)
    i = 0
    _size = respond_to?(:size) ? size : nil
    each{|e|
      yield(e) if (filter.include?(i) || (_size && filter.include(i - _size)))
      i += 1
    }
  else
    raise ArgumentError
  end
end
each_at_normalize_filter(filter) click to toggle source

Normalize the filter to make it respond to each and include?.

If we can guarantee the filter is all whole numbers, then subsequent method calls can optimize the index lookup, by looking for the whole numbers instead of negative numbers.

# File lib/sixarm_ruby_ramp/enumerable/each.rb, line 131
def each_at_normalize_filter(filter)
  case filter
  when Numeric
    return [filter.to_i], filter >= 0
  when Range
    if filter.first < 0 || filter.last < 0
      min = filter.first.to_i; min += size if min < 0
      max = filter.last.to_i; max += size if max < 0
      max -= 1 if filter.exclude_end?
      filter = min..max
    end
    return filter, true
  else
    return filter, false
  end
end
each_at_strategy_with_optimization_max(filter) { |at(i)| ... } click to toggle source

Implement each_at by using a strategy with full optimization.

When each_at tests that both self#at is available and filter#each is available, and can test that all indexes are whole numbers, then each_at calls this strategy.

# File lib/sixarm_ruby_ramp/enumerable/each.rb, line 119
def each_at_strategy_with_optimization_max(filter)
  filter.each{|i|
    yield(at(i))
  }
end
each_at_strategy_with_optimization_min(filter) { |at(i >= 0 ? i : i + size)| ... } click to toggle source

Implement each_at by using a strategy with some optimization.

When each_at tests that both self#at is available and filter#each is available, yet cannot test that all indexes are whole numbers, then each_at calls this strategy.

# File lib/sixarm_ruby_ramp/enumerable/each.rb, line 107
def each_at_strategy_with_optimization_min(filter)
  filter.each{|i|
    yield(at(i >= 0 ? i : i + self.size))
  }
end
each_at_strategy_with_optimization_off(filter) { |e| ... } click to toggle source

Implement each_at by using a strategy with no optimization.

When each_at tests that either self#at is unavailable or filter#each is unavailable, then each_at calls this strategy.

This strategy uses a loop counter and iteration on the self elements, and each iteration, test whether the counter is in the filter.

This strategy is the slowest, and for the worst-case need. This strategy is rarely needed in the wild.

# File lib/sixarm_ruby_ramp/enumerable/each.rb, line 86
def each_at_strategy_with_optimization_off(filter)
  i = 0
  if respond_to?(:size)
    each{|e|
      yield(e) if (filter.include?(i) || (size && filter.include?(i - size)))
      i += 1
    }
  else
    each{|e|
      yield(e) if (filter.include?(i))
      i += 1
    }
  end
end
hash_by() { |item| ... } click to toggle source

Convert the enumerable to a hash by mapping each item to a pair [item, new item]

@return [Hash<Object,Object>] a hash of the enumerable's items

@example

strings = ["red","blue","green"]
strings.hash_by{|a| [a.size, a.upcase]}
=> {3 => "RED", 4 => "BLUE", 5 => "GREEN"}

@see index_by

# File lib/sixarm_ruby_ramp/enumerable.rb, line 94
def hash_by
  map{|item| yield(item)}.to_h
end
index_by() { |elem| ... } click to toggle source

Convert the enumerable to a hash by mapping each item to a pair [index ,item]

@return [Hash<Integer,Object>] a hash of the enumerable's items

@example

strings = ["red","blue","green"]
strings.index_by{|a| a.size]}
=> {3 => "red", 4 => "blue", 5 => "green"}

Rails has this method.

From stackoverflow.com/questions/412771/cleanest-way-to-create-a-hash-from-an-array

@see hash_by

# File lib/sixarm_ruby_ramp/enumerable.rb, line 79
def index_by
  inject({}) {|hash, elem| hash.merge!(yield(elem) => elem) }
end
intersect?(enum) click to toggle source

@return [Boolean] true if this enum intersects another enum.

A developer may want to optimize this implementation for other classes, such as detecting whether a range intersects another range simply by comparing the ranges' min/max values.

@example

['a','b','c'].intersect?(['c','d','e'] => true
['a','b','c'].intersect?(['d','e','f'] => false
# File lib/sixarm_ruby_ramp/enumerable.rb, line 180
def intersect?(enum)
  return enum.any?{|item| self.include?(item)}
end
join(*op) click to toggle source

Shortcut to Array#join to concatenate the items into a string

@example

["foo", "goo", "hoo"].join
=> "foogoohoo"

@return [String] concatenated string

@see Array#join

# File lib/sixarm_ruby_ramp/enumerable.rb, line 160
def join(*op)
 to_a.join(*op)
end
map_id() click to toggle source

Map each item => item.id

@return [Enumerable<Object>] an list of each item.id

@example

users = User.find(:all)
users.map_id => [1,2,3,4,...]

A typical use is to convert a list of ActiveRecord items to a list of id items.

This method is a faster way to get the same results as items.map(&:id)

# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 15
def map_id
  map{|item| item.id}
end
map_to_a() click to toggle source

Map each item => item.to_a

@return [Enumberable<Array<Object>>] a list of each item.to_a

@example

hashes = [{1=>2, 3=>4},{5=>6, 7=>8}]
hashes.map_to_a => [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

This method is a faster way to get the same results as items.map(&:to_a)

# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 29
def map_to_a
  map{|item| item.to_a}
end
map_to_f() click to toggle source

Map each item => item.to_f

@return [Enumerable<Float>] a list of each item.to_f

@example

strings = ["1","2","3"]
strings.map_to_f => [1.0, 2.0, 3.0]

A typical use is to convert a list of String items to a list of float items.

This method is a fast way to get the same results as items.map(&:to_f)

# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 45
def map_to_f
  map{|item| item.to_f}
end
map_to_i() click to toggle source

Map each item => item.to_i

@return [Enumerable<Integer>] a list of each item.to_i

@example

strings = ["1","2","3"]
strings.map_to_i => [1, 2, 3]

A typical use is to convert a list of String items to a list of integer items.

This method is a fast way to get the same results as items.map(&:to_i)

# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 61
def map_to_i
  map{|item| item.to_i}
end
map_to_s() click to toggle source

Map each item => item.to_s

@return [Enumerable<String>] a list of each item.to_s

@example

numbers = [1, 2, 3]
numbers.map_to_s => ["1", "2", "3"]

A typical use is to convert a list of Numeric items to a list of String items.

This method is a fast way to get the same results as items.map(&:to_s)

# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 77
def map_to_s
  map{|item| item.to_s}
end
map_to_sym() click to toggle source

Map each item => item.to_sym

@return [Enumerable<Symbol>] a list of each item.to_sym

@example

strings = ["foo", "goo", "hoo"]
strings.map_to_sym => [:foo, :goo, :hoo]

A typical use is to convert a list of Object items to a list of Symbol items.

This method is a fast way to get the same results as items.map(&:to_sym)

# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 93
def map_to_sym
  map{|item| item.to_sym}
end
map_with_index() { |item,index| ... } click to toggle source

Map each item and its index => a new output

@return [Enumerable<Object>] an list of each item transformed by the block @see Enumerable#map @see Enumerable#each_with_index

@example

strings = ["a", "b", "c"]
strings.map_with_index{|string,index| "#{string}#{index}"}
 => ["a0, "b1", "c3"]
# File lib/sixarm_ruby_ramp/enumerable/map.rb, line 108
def map_with_index
  index=-1
  map{|item| index+=1; yield(item,index)}
end
mutex?() { |item| ... } click to toggle source

@example

enum.mutex? {|obj| block}
=> true iff block is not false or nil, zero or one time

@return boolean true iff block is not false or nil, zero or one time

# File lib/sixarm_ruby_ramp/enumerable.rb, line 137
def mutex?
  num = 0
  each{|item|
    if yield(item)
      num += 1
      if num > 1 then return false end
    end
  }
  return true
end
nitems?(n) { |item| ... } click to toggle source

@example

enum.nitems?(n) {| obj | block }
 => true iff the block is not false or nil num times

@return [Boolean] true iff the block is not false or nil num times

# File lib/sixarm_ruby_ramp/enumerable/nitems.rb, line 9
def nitems?(n)
  num = 0
  each{|item|
    if yield(item)
      num+=1
      if num > n then return false end
    end
  }
  return num==n
end
nitems_until() { |item| ... } click to toggle source

@example

enum.nitems_until {| obj | block }
=> number of items

@return [Integer] the number of leading elements for which block is false.

# File lib/sixarm_ruby_ramp/enumerable/nitems.rb, line 38
def nitems_until
  num = 0
  each{|item|
    if yield(item)
      break
    else
      num+=1
    end
  }
  return num
end
nitems_while() { |item| ... } click to toggle source

@example

enum.nitems_while {| obj | block }
 => number of items

@return [Integer] the number of leading elements for which block is not false or nil.

# File lib/sixarm_ruby_ramp/enumerable/nitems.rb, line 26
def nitems_while
  num = 0
  each{|item| yield(item) ? (num+=1) : break}
  return num
end
nitems_with_index() { |item,index| ... } click to toggle source

Calls block with two arguments, the item and its index, for each item in enum.

@example

enum.nitems_with_index {|obj,i| block }
 => number of items

@return [Integer] the number of leading elements for which block is true.

# File lib/sixarm_ruby_ramp/enumerable/nitems.rb, line 59
def nitems_with_index
  index = 0
  each{|item| yield(item,index) ? (index+=1) : break}
  return index
end
power_set() click to toggle source

Calculate the power set.

@return [Array<Array<Object>>] the power set: an array with all subsets of the enum's elements. @see en.wikipedia.org/wiki/Power_set

This implementation is from the blog post below: @see johncarrino.net/blog/2006/08/11/powerset-in-ruby/

@example

[1,2,3].power_set.sort
=>  [[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]
# File lib/sixarm_ruby_ramp/enumerable.rb, line 230
def power_set
  inject([[]]){|c,y|r=[];c.each{|i|r<<i;r<<i+[y]};r}
end
select_until() { |item| ... } click to toggle source

@example

enum.select_until {|obj| block }
 => array

@return [Array<Object>] the leading elements for which block is falsey.

# File lib/sixarm_ruby_ramp/enumerable/select.rb, line 21
def select_until
  arr = []
  each{|item| yield(item) ? break : (arr << item)}
  return arr
end
select_while() { |item| ... } click to toggle source

@example

enum.select_while {|obj| block }
 => array

@return [Array<Object>] the leading elements for which block is truthy.

# File lib/sixarm_ruby_ramp/enumerable/select.rb, line 9
def select_while
  arr = []
  each{|item| yield(item) ? (arr << item) : break}
  return arr
end
select_with_index() { |item,index| ... } click to toggle source

Calls block with two arguments, the item and its index, for each item in enum.

@example

enum.select_with_index {|obj,i| block }
=> array

@return [Array<Object> the leading elements for which block is truthy.

# File lib/sixarm_ruby_ramp/enumerable/select.rb, line 35
def select_with_index
  index = 0
  arr = []
  each{|item|
    if yield(item,index)
      arr << item
      index+=1
    else
      break
    end
  }
  return arr
end
to_h() click to toggle source
# File lib/sixarm_ruby_ramp/enumerable.rb, line 21
def to_h
  hash={}
  each{|key,val|
    hash[key]=val
  }
  return hash
end
to_h_merge() click to toggle source

Convert an enumerable to a hash and merge values.

@return [Hash<Object,Object>] a hash of the enumerable's items

@example

array=[[:a, :b],[:c, :d],[:e, :f]]
array.to_h => {:a=>:b, :c=>:d, :e=>:f}

If a key occurs more than once, then this will automatically merge the values to an array of the keys' values.

@example

array=[[:a,:b],[:a,:c],[:a,:d]]
array.to_h => {:a=>[:b, :c, :d]}
# File lib/sixarm_ruby_ramp/enumerable.rb, line 45
def to_h_merge
  hash={}
  seen=Set.new
  each{|key,val|
    if hash.key? key
      if seen.include? key
        hash[key] << val
      else
        hash[key]=[hash[key]]
        hash[key] << val
        seen << key
      end
    else
      hash[key]=val
    end
  }
  return hash
end