module Enumerable

This is a simple reimplementation of the core Enumerable module to allow the methods to take and pass-on arbitrary arguments to the underlying each call. This library uses Enumerator and scans Enumerable so it can alwasy stay in sync.

NOTE Any Enumerable method with a negative arity cannot do pass arguments due to ambiguity in the argument count. So the methods inject and zip do NOT work this way, but simply work as they do in Enumerable. The method find (and detect) though has been made to work by removing its rarely used optional parameter and providing instead an optional keyword parameter (:ifnone => …). Please keep these difference in mind.

require 'enumargs'

class T
  include Enumerable::Arguments
  def initialize(arr)
    @arr = arr
  end
  def each(n)
    arr.each{ |e| yield(e+n) }
  end
end

t = T.new([1,2,3])
t.collect(4)
#=> [5,6,7]

Public Instance Methods

accumulate() click to toggle source

Accumulate a set of a set.

For example, in an ORM design if Group has_many User then

groups.accumulate.users

will return a list of users from all groups.

CREDIT: George Moshchovitis

# File lib/core/facets/enumerable/accumulate.rb, line 16
def accumulate
  @_accumulate ||= Functor.new do |op, *args|
    inject([]) { |a, x| a << x.send(op, *args) }.flatten
  end
end
cluster_by(&b) click to toggle source

Similar to group_by but returns an array of the groups. Returned elements are sorted by block.

%w{this is a test}.cluster_by {|x| x[0]}

produces

[ ['a'], ['is'], ['this', 'test'] ]

CREDIT: Erik Veenstra

# File lib/core/facets/enumerable/cluster_by.rb, line 16
def cluster_by(&b)
  group_by(&b).sort.transpose.pop || []   # group_by(&b).values ?
end
collect_with_index()

Alias for map_with_index.

Alias for: map_with_index
commonality( &block ) click to toggle source

Returns all items that are equal in terms of the supplied block. If no block is given objects are considered to be equal if they return the same value for Object#hash and if obj1 == obj2.

[1, 2, 2, 3, 4, 4].commonality # => { 2 => [2], 4 => [4] }

["foo", "bar", "a"].commonality { |str| str.length }
# => { 3 => ["foo, "bar"] }

# Returns all persons that share their last name with another person.
persons.collisions { |person| person.last_name }

CREDIT: Florian Gross

# File lib/core/facets/enumerable/commonality.rb, line 17
def commonality( &block )
  had_no_block = !block
  block ||= lambda { |item| item }
  result = Hash.new { |hash, key| hash[key] = Array.new }
  self.each do |item|
    key = block.call(item)
    result[key] << item
  end
  result.reject! do |key, values|
    values.size <= 1
  end
  #return had_no_block ? result.values.flatten : result
  return result
end
compact_collect(trash=nil, &block)
Alias for: compact_map
compact_map(trash=nil) { |*a| ... } click to toggle source

A more versitle compact method. It can be used to collect and filter items out in one single step.

[1,2,3].compact_map do |n|
  n < 1 ? nil : n
end

produces

[2,3]

NOTE: Perhaps nicer to have as added functionality for compact.

CREDIT: Trans

# File lib/core/facets/enumerable/compact_map.rb, line 18
def compact_map(trash=nil, &block)
  y = []
  if block_given?
    each do |*a|
      r = yield(*a)
      y << r unless trash == r
    end
  else
    each do |r|
      y << r unless trash == r
    end
  end
  y
end
Also aliased as: compact_collect
count(*items, &block) click to toggle source

Count the number of items in an enumerable equal (==) to the given object(s).

e = [ 'a', '1', 'a' ]
e.count('1')     #=> 1
e.count('a')     #=> 2
e.count('a', 1)  #=> 3

Note that Hash#count only considers values.

e = { 'a' => 2, 'x' => 2, 'b' => 1 }
e.count(1) #=> 1
e.count(2) #=> 2

CREDIT: Trans

# File lib/core/facets/enumerable/count.rb, line 23
def count(*items, &block)
  if block || !items.empty?
    r = self
    r = r.select(&block) if block
    r = r.select{ |x| items.any?{ |i| i == x } } if !items.empty?
    r.size
  else
    begin
      size
    rescue
      i=0; each{ |e| i+=1 }; i
    end
  end
end
defer() { |output, *input| ... } click to toggle source

Without a block: wrap the Enumerable object in such a way that map, select and similar operations are performed “horizontally” across a series of blocks, instead of building an array of results at each step. This reduces memory usage, allows partial results to be provided early, and permits working with infinite series.

a = (1..1_000_000_000).defer.select{ |i| i % 2 == 0 }.
                             map{ |i| i + 100 }.
                             take(10).to_a

With a block: the block acts as an arbitrary filter on the data. Unlike map, it can choose to drop elements from the result, and/or add additional ones. The first object passed to the block is the receiver of the output.

(1..1_000_000_000).
  defer { |out,i| out << i if i % 2 == 0 }.  # like select
  defer { |out,i| out << i + 100 }.          # like map
  take(10).
  each { |i| puts i }

Using with a block, defer(&b), is equivalent to:

defer.filter(&b)

Use a method like to_a or #to_h at the end of the chain when you want an Array or Hash built with the results, or each{…} if you just want to output each result and discard it.

# File lib/core/facets/enumerable/defer.rb, line 34
def defer(&blk)
  if block_given?
    Denumerator.new do |output|
      each do |*input|
        yield output, *input
      end
    end
  else
    Denumerator.new do |output|
      each do |*input|
        output.yield *input
      end
    end
  end
end
divide(pattern) click to toggle source

Divide on matching pattern.

['a1','b1','a2','b2'].divide(/^a/)
=> [['a1,'b1'],['a2','b2']]

CREDIT: Trans

# File lib/core/facets/enumerable/divide.rb, line 10
def divide(pattern)
  memo = []
  each do |obj|
    memo.push [] if pattern === obj
    memo.last << obj
  end
  memo
end
each_by(steps=nil, &block) click to toggle source

Iterate through slices. If slice steps is not given, the arity of the block is used.

x = []
[1,2,3,4].each_by{ |a,b| x << [a,b] }
x  #=> [ [1,2], [3,4] ]

x = []
[1,2,3,4,5,6].each_by(3){ |a| x << a }
x  #=> [ [1,2,3], [4,5,6] ]

This is just like each_slice, except that it will check the arity of the block. If each_slice ever suppots this this method can be deprecated.

CREDIT: Trans

# File lib/core/facets/enumerable/each_by.rb, line 22
def each_by(steps=nil, &block)
  if steps
    each_slice(steps, &block)
  else
    steps = block.arity.abs
    each_slice(steps, &block)
    #each_slice(steps) {|i| block.call(*i)}
  end
end
elementwise(count=1) click to toggle source

Returns an elementwise Functor designed to make R-like elementwise operations possible.

[1,2].elementwise + 3          #=> [4,5]
[1,2].elementwise + [4,5]      #=> [5,7]
[1,2].elementwise + [[4,5],3]  #=> [[5,7],[4,5]
# File lib/core/facets/enumerable/ewise.rb, line 16
def elementwise(count=1)
  @_elementwise_functor ||= []
  @_elementwise_functor[count] ||= Functor.new do |op,*args|
    if args.empty?
      r = self
      count.times do
        r = r.collect{ |a| a.send(op) }
      end
      r
    else
      r = args.collect do |arg|
        if Array === arg #arg.kind_of?(Enumerable)
          x = self
          count.times do
            ln = (arg.length > length ? length : arg.length )
            x = x.slice(0...ln).zip(arg[0...ln]).collect{ |a,b| a.send(op,b) }
            #slice(0...ln).zip(arg[0...1n]).collect{ |a,b| b ? a.send(op,b) : nil }
          end
          x
        else
          x = self
          count.times do
            x = x.collect{ |a| a.send(op,arg) }
          end
          x
        end
      end
      r.flatten! if args.length == 1
      r
    end
  end
end
Also aliased as: ewise
entropy() click to toggle source

Shannon's entropy for an array - returns the average bits per symbol required to encode the array. Lower values mean less “entropy” - i.e. less unique information in the array.

%w{ a b c d e e e }.entropy  #=>

CREDIT: Derek

# File lib/core/facets/enumerable/entropy.rb, line 14
def entropy
  arr = to_a
  probHash = arr.probability
  # h is the Shannon entropy of the array
  h = -1.to_f * probHash.keys.inject(0.to_f) do |sum, i|
    sum + (probHash[i] * (Math.log(probHash[i])/Math.log(2.to_f)))
  end
  h
end
every() click to toggle source

Returns an elemental object. This allows you to map a method on to every element.

r = [1,2,3].every + 3  #=> [4,5,6]
# File lib/core/facets/enumerable/every.rb, line 10
def every
  @_every ||= per(:map)
end
every!() click to toggle source

In place version of every.

# File lib/core/facets/enumerable/every.rb, line 16
def every!
  raise NoMethodError unless respond_to?(:map!)
  @_every_inplace ||= per(:map!)
end
ewise(count=1)

Concise alias for elementwise.

a = [1,2]
a.ewise + 3          #=> [4,5]
a.ewise + [4,5]      #=> [5,7]
a.ewise + [[4,5],3]  #=> [[5,7],[4,5]

Note this used to be ew as weel as the '%' operator. Both of whihc are deprecated.

Alias for: elementwise
filter(output=[]) { |output, *input| ... } click to toggle source

The block acts as an arbitrary filter on the data. Unlike map, it can choose to drop elements from the result, and/or add additional ones. The first object passed to the block is the receiver of the output.

(1..1_000_000_000).
  filter { |out,i| out << i if i % 2 == 0 }.  # like select
  filter { |out,i| out << i + 100 }.          # like map
  take(10).each { |i| puts i }
# File lib/core/facets/enumerable/filter.rb, line 13
def filter(output=[])
  if block_given?
    each do |*input|
      yield(output, *input)
    end
    output
  else
    to_enum(:filter)
  end
end
graph(&yld)

Alias for mash. This is the original name of this method.

Alias for: mash
group_by() { || ... } click to toggle source

group_by is used to group items in a collection by something they have in common. The common factor is the key in the resulting hash, the array of like elements is the value.

(1..5).group_by { |n| n % 3 }
     #=> { 0 => [3], 1 => [1, 4], 2 => [2,5] }

["I had", 1, "dollar and", 50, "cents"].group_by { |e| e.class }
     #=> { String => ["I had","dollar and","cents"], Fixnum => [1,50] }

CREDIT: Erik Veenstra

# File lib/core/facets/enumerable/group_by.rb, line 17
def group_by #:yield:
  #h = k = e = nil
  r = Hash.new
  each{ |e| (r[yield(e)] ||= []) << e }
  r
end
ideal_entropy() click to toggle source

Returns the maximum possible Shannon entropy of the array with given size assuming that it is an “order-0” source (each element is selected independently of the next).

CREDIT: Derek

# File lib/core/facets/enumerable/entropy.rb, line 30
def ideal_entropy
  arr = to_a
  unitProb = 1.0.to_f / arr.size.to_f
  (-1.to_f * arr.size.to_f * unitProb * Math.log(unitProb)/Math.log(2.to_f))
end
inject!(s) { |k, i| ... } click to toggle source

A small variation of #Inject that save one from having to return the aggregating or memo argument.

Say you want to count letters.

some_text.inject!(Hash.new(0)) {|h,l| h[l] += 1}

vs

some_text.inject(Hash.new(0)) {|h,l| h[l] +=1; h}

CREDIT: David Black, Louis J Scoras

# File lib/core/facets/enumerable/inject.rb, line 16
def inject!(s)
  k = s
  each { |i| yield(k, i) }
  k
end
map_detect(value_for_none_matching = nil) { |member| ... } click to toggle source

Yield each element to the block. Returns the result of the block when the block is true, terminating early as detect does.

obj1.foo? #=> false
obj2.foo? #=> true

obj2.foo  #=> "a value"

[obj1, obj2].map_detect { |obj| obj.foo if obj.foo? } #=> "a value"

If the block is never true, return the object given in the first parameter, or nil if none specified.

[1,2,3].map_detect { |_| false }    #=> nil
[false].map_detect(1) { |_| false } #=> 1
# File lib/core/facets/enumerable/map_detect.rb, line 19
def map_detect(value_for_none_matching = nil)
  each do |member|
    if result = yield(member)
      return result
    end
  end
  
  value_for_none_matching
end
map_send(meth, *args, &block) click to toggle source

Send a message to each element and collect the result.

CREDIT: Sean O'Halpin

# File lib/core/facets/enumerable/map_send.rb, line 7
def map_send(meth, *args, &block)
  map{|e| e.send(meth, *args, &block)}
end
map_with_index() { |self, i| ... } click to toggle source

Same as collect but with an iteration counter.

a = [1,2,3].collect_with_index { |e,i| e*i }
a  #=> [0,2,6]

CREDIT: Gavin Sinclair

# File lib/core/facets/enumerable/map_with_index.rb, line 10
def map_with_index
  r = []
  each_index do |i|
    r << yield(self[i], i)
  end
  r
end
Also aliased as: collect_with_index
mash(&yld) click to toggle source

Like #map/#collect, but generates a Hash. The block is expected to return two values: the key and the value for the new hash.

numbers  = (1..3)
squares  = numbers.mash { |n| [n, n*n] }   # { 1=>1, 2=>4, 3=>9 }
sq_roots = numbers.mash { |n| [n*n, n] }   # { 1=>1, 4=>2, 9=>3 }

The name “mash” stands for “map hash”.

CREDIT: Andrew Dudzik (adudzik), Trans

# File lib/core/facets/enumerable/mash.rb, line 14
def mash(&yld)
  if yld
    h = {}
    each do |*kv|
      r = yld[*kv]
      case r
      when Hash
        nk, nv = *r.to_a[0]
      when Range
        nk, nv = r.first, r.last
      else
        nk, nv = *r
      end
      h[nk] = nv
    end
    h
  else
    Enumerator.new(self,:mash)
  end
end
Also aliased as: graph
mode() click to toggle source

In Statistics mode is the value that occurs most frequently in a given set of data.

CREDIT: Robert Klemme

# File lib/core/facets/enumerable/mode.rb, line 8
def mode
  max = 0
  c = Hash.new 0
  each {|x| cc = c[x] += 1; max = cc if cc > max}
  c.select {|k,v| v == max}.map {|k,v| k}
end
modulate(modulo) click to toggle source

Modulate. Divide an array into groups by modulo of the index.

2,4,6,8].modulate(2) #=> [[2,6],

CREDIT: Trans

NOTE: Would the better name for this be 'collate'?

# File lib/core/facets/enumerable/modulate.rb, line 11
def modulate(modulo)
  return to_a if modulo == 1
  raise ArgumentError, 'bad modulo' if size % modulo != 0
  r = Array.new(modulo, [])
  (0...size).each do |i|
    r[i % modulo] += [self[i]]
  end
  r
end
none?() { |e| ... } click to toggle source

#none? is the logical opposite of the builtin method Enumerable#any?. It returns true if and only if none of the elements in the collection satisfy the predicate.

If no predicate is provided, #none? returns true if and only if none of the elements have a true value (i.e. not nil or false).

[].none?                      # true
[nil].none?                   # true
[5,8,9].none?                 # false
(1...10).none? { |n| n < 0 }  # true
(1...10).none? { |n| n > 0 }  # false

CREDIT: Gavin Sinclair

# File lib/core/facets/enumerable/none.rb, line 21
def none?  # :yield: e
  if block_given?
    not self.any? { |e| yield e }
  else
    not self.any?
  end
end
occur(n=nil) { || ... } click to toggle source

Returns an array of elements for the elements that occur n times. Or according to the results of a given block.

[1,1,2,3,3,4,5,5].occur(1)             #=> [2,4]
[1,1,2,3,3,4,5,5].occur(2)             #=> [1,3,5]
[1,1,2,3,3,4,5,5].occur(3)             #=> []

[1,2,2,3,3,3].occur(1..1)              #=> [1]
[1,2,2,3,3,3].occur(2..3)              #=> [2,3]

[1,1,2,3,3,4,5,5].occur { |n| n == 1 } #=> [2,4]
[1,1,2,3,3,4,5,5].occur { |n| n > 1 }  #=> [1,3,5]
# File lib/core/facets/enumerable/occur.rb, line 16
def occur(n=nil) #:yield:
  result = Hash.new { |hash, key| hash[key] = Array.new }
  self.each do |item|
    key = item
    result[key] << item
  end
  if block_given?
    result.reject! { |key, values| ! yield(values.size) }
  else
    raise ArgumentError unless n
    if Range === n
      result.reject! { |key, values| ! n.include?(values.size) }
    else
      result.reject! { |key, values| values.size != n }
    end
  end
  return result.values.flatten.uniq
end
one?() { |e| ... } click to toggle source

#one? returns true if and only if exactly one element in the collection satisfies the given predicate.

If no predicate is provided, #one? returns true if and only if exactly one element has a true value (i.e. not nil or false).

[].one?                      # false
[nil].one?                   # false
[5].one?                     # true
[5,8,9].one?                 # false
(1...10).one? { |n| n == 5 } # true
(1...10).one? { |n| n < 5 }  # false

CREDIT: Gavin Sinclair

# File lib/core/facets/enumerable/one.rb, line 21
def one?  # :yield: e
  matches = 0
  if block_given?
    self.each do |e|
      if yield(e)
        matches += 1
        return false if matches > 1
      end
    end
    return (matches == 1)
  else
    one? { |e| e }
  end
end
per(enum_method=nil, *enum_args) click to toggle source

Per element meta-functor.

[1,2,3].per + 3             #=> [4,5,6]
[1,2,3].per(:map) + 3       #=> [4,5,6]
[1,2,3].per(:select) > 1    #=> [2,3]

[1,2,3].per.map + 3       #=> [4,5,6]
[1,2,3].per.select > 1    #=> [2,3]
# File lib/core/facets/enumerable/per.rb, line 18
def per(enum_method=nil, *enum_args)
  if enum_method
    Permeator.new(self, enum_method, *enum_args)
    #Functor.new do |op, *args|
    #  __send__(enum_method, *enum_args){ |x| x.__send__(op, *args) } #, &blk) }
    #end
  else
    @__per__ ||= Functor.new do |enum_method, *enum_args|
      Permeator.new(self, enum_method, *enum_args)
      #Functor.new do |op, *args|
      #  __send__(enum_method, *enum_args){ |x| x.__send__(op, *args) } #, &blk) }
      #end
    end
  end
end
split(pattern) click to toggle source

Split on matching pattern. Unlike divide this does not include matching elements.

['a1','a2','b1','a3','b2','a4'].split(/^b/)
=> [['a1','a2'],['a3'],['a4']]

CREDIT: Trans

# File lib/core/facets/enumerable/split.rb, line 10
def split(pattern)
  memo = []
  sect = []
  each do |obj|
    if pattern === obj
      memo << sect
      sect = []
    else
      sect << obj
    end
  end
  memo << sect
  memo.pop while memo.last == []
  memo
end
sum(identity = 0, &block) click to toggle source

Uses #+ to sum the enumerated elements.

[1,2,3].sum  #=> 6
[3,3,3].sum  #=> 9
# File lib/core/facets/enumerable/sum.rb, line 8
def sum(identity = 0, &block)
  if block_given?
    map(&block).sum
  else
    inject{ |sum, element| sum + element } || identity
  end
end
take(n) click to toggle source

Return the first n items from the collection

# File lib/core/facets/enumerable/take.rb, line 7
def take(n)
  res = []
  count = 0
  each do |e|
    break if count >= n
    res << e
    count += 1
  end
  res
end
threaded_map() { || ... } click to toggle source

Like Enumerable#map but each iteration is processed via a separate thread.

CREDIT Sean O'Halpin

# File lib/more/facets/thread.rb, line 37
def threaded_map #:yield:
  map{ |e| Thread.new(e){ |t| yield(t) } }.map{ |t| t.value }
end
threaded_map_send(meth, *args, &block) click to toggle source

Like #map_send but each iteration is processed via a separate thread.

CREDIT Sean O'Halpin

# File lib/more/facets/thread.rb, line 46
def threaded_map_send(meth, *args, &block)
  map{ |e| Thread.new(e){ |t| t.send(meth, *args, &block) } }.map{ |t| t.value }
end
to_h(mode=nil) click to toggle source

Convert an Enumerable object into a hash by first turning it into an array.

CREDIT: Trans

# File lib/core/facets/to_hash.rb, line 225
def to_h(mode=nil)
  to_a.to_h(mode)
end
to_h_assoc() click to toggle source
# File lib/core/facets/to_hash.rb, line 241
def to_h_assoc
  to_a.to_h_assoc
end
to_h_auto() click to toggle source
# File lib/core/facets/to_hash.rb, line 229
def to_h_auto
  to_a.to_h_auto
end
to_h_flat() click to toggle source
# File lib/core/facets/to_hash.rb, line 237
def to_h_flat
  to_a.to_h_flat
end
to_h_multi() click to toggle source
# File lib/core/facets/to_hash.rb, line 245
def to_h_multi
  to_a.to_h_multi
end
to_h_splat() click to toggle source
# File lib/core/facets/to_hash.rb, line 233
def to_h_splat
  to_a.to_h_splat
end
uniq_by() { || ... } click to toggle source

Like uniq, but determines uniqueness based on a given block.

(-5..5).to_a.uniq_by {|i| i*i }

produces

[-5, -4, -3, -2, -1, 0]
# File lib/core/facets/enumerable/uniq_by.rb, line 11
def uniq_by #:yield:
  h = {}; inject([]) {|a,x| h[yield(x)] ||= a << x}
end