module Enumerable

Constants

Arguments

This is a simple reimplementation of the core Enumerable module to allow its 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 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. However, The methods find and detect have been made modified to work by removing its rarely used optional parameter and providing instead an optional keyword parameter (:ifnone => …). Please keep these difference in mind.

Example

require 'enumargs'

class T
  include Enumerable::Argumentable
  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(iterations=1) click to toggle source

Accumulate a set of a set. For example, in an ORM design where ‘Group has_many User` we might have something equivalent to the following.

Group = Struct.new(:users)
User  = Struct.new(:name, :friends)

user1 = User.new('John', [])
user2 = User.new('Jane', ['Jill'])
user3 = User.new('Joe' , ['Jack', 'Jim'])

group1 = Group.new([user1, user2])
group2 = Group.new([user2, user3])

groups = [group1, group2]

Now we can accumulate the users of all groups.

groups.accumulate.users  #=> [user1, user2, user3]

You may pass an argument to perform chains, e.g. the following returns the names of users from all groups.

groups.accumulate(2).users.name  #=> ['John','Jane','Joe']

Or we can gather all the friends of all users in groups.

groups.accumulate(2).users.friends  #=> ['Jill','Jack','Jim']

This is more convenient then the equivalent.

groups.accumulate.users.accumulate.friends  #=> ['Jill','Jack','Jim']

CREDIT: George Moshchovitis, Daniel Emirikol, Robert Dober

# File lib/core/facets/enumerable/accumulate.rb, line 42
def accumulate(iterations=1)
  return self if iterations == 0

  Functor.new do |op, *args|
    result = []
    each { |x| result << x.send(op, *args) }
    result.flatten.uniq.accumulate(iterations - 1)
  end
end
accumulate_all(iterations=1) click to toggle source

Same as accumulate, but does not apply uniq to final result.

groups.accumulate_all(2).users.friends  #=> ['Jill', 'Jill','Jack','Jim']
# File lib/core/facets/enumerable/accumulate.rb, line 56
def accumulate_all(iterations=1)
  return self if iterations == 0

  Functor.new do |op, *args|
    result = []
    each { |x| result << x.send(op, *args) }
    result.flatten.accumulate_all(iterations - 1)
  end
end
apply() click to toggle source

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

r = [1,2,3].apply.+ 
r  #=> 6
# File lib/core/facets/enumerable/apply.rb, line 12
def apply
  #Functor.new do |sym, *args, &blk|
  #  inject{ |r, e| r.__send__(sym, e, *args, &blk) }
  #end
  per(:inject)
end
associate(missing=nil)

Alias for pair.

Alias for: pair
cluster() { |last.last| ... } click to toggle source

Clusters together adjacent elements into a list of sub-arrays.

[2,2,2,3,3,4,2,2,1].cluster{ |x| x }
=> [[2, 2, 2], [3, 3], [4], [2, 2], [1]]

["dog", "duck", "cat", "dude"].cluster{ |x| x[0] }
=> [["dog", "duck"], ["cat"], ["dude"]]

@author Oleg K

# File lib/core/facets/enumerable/cluster.rb, line 13
def cluster
  cluster = []
  each do |element|
    if cluster.last && yield(cluster.last.last) == yield(element)
      cluster.last << element
    else
      cluster << [element]
    end
  end
  cluster
end
cluster_by(&b)

DEPRECATED: Name changed to avoid confusion with cluster.

Alias for: organize_by
collect_with_index()

Alias for map_with_index.

Alias for: map_with_index
compact_collect(&block)
Alias for: compact_map
compact_map() { |*a| ... } click to toggle source

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

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

c  #=> [2,3]

CREDIT: Trans

DEPRECATE: This method should probably be removed b/c purge does almost the same thing and enum.map{}.compact works too.

# File lib/core/facets/enumerable/compact_map.rb, line 17
def compact_map(&block)
  y = []
  if block_given?
    each do |*a|
      r = yield(*a)
      y << r unless r.nil?
    end
  else
    each do |r|
      y << r unless r.nil?
    end
  end
  y
end
Also aliased as: compact_collect
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).to_a

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

Long-term for ewise.

a = [1,2]
(a.elementwise + 3)          #=> [4,5]
(a.elementwise + [4,5])      #=> [5,7]
Alias for: ewise
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
r  #=> [4,5,6]
# File lib/core/facets/enumerable/every.rb, line 11
def every
  per(:map)
end
every!() click to toggle source

In place version of every.

# File lib/core/facets/enumerable/every.rb, line 17
def every!
  raise NoMethodError unless respond_to?(:map!)
  per(:map!)
end
ewise(count=1) click to toggle source

Returns an elementwise Functor designed to make R-like elementwise operations possible. This is very much like the every method, but it treats array argument specially.

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

Vector to vector

([1,2].ewise + [4,5])      #=> [5,7]

Special thanks to Martin DeMello for helping to develop this.

# File lib/core/facets/enumerable/ewise.rb, line 19
def ewise(count=1)
  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)
            x = x.zip(arg[0...ln])
            x = x.collect{ |a,b| a.send(op,b) }  #x = x.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: elementwise
exclude?(object) click to toggle source

The inverse of include?.

[:a, :b].exclude?(:c)  #=> true
[:a, :b].exclude?(:a)  #=> false
# File lib/core/facets/enumerable/exclude.rb, line 10
def exclude?(object)
  !include?(object)
end
expand() click to toggle source

Expand all elements of an Enumerable object.

[0, 2..3, 5..7].expand  #=> [0,[2, 3],[5,6,7]]

CREDIT: Trans

# File lib/core/facets/enumerable/expand.rb, line 8
def expand
  map do |x|
   (Enumerable === x ? x.expand : x)
  end
end
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 elements. The first object passed to the block is the receiver of the output.

x = (1..10000)
x = x.filter{ |out,i| out << i if i % 2 == 0 }   # like select
x = x.filter{ |out,i| out << i + 100 }           # like map
x = x.take(3)

x  #=> [102, 104, 106]

This is very similar to each_with_object, but filter handles argument better by reversing their order and using the splat operator. (This was also once known as injecting.)

CREDIT: David Black, Louis J Scoras

# File lib/core/facets/enumerable/filter.rb, line 21
def filter(output=[]) #:yeild:
  if block_given?
    each do |*input|
      yield(output, *input)
    end
    output
  else
    to_enum(:filter)
  end
end
find_yield(fallback=nil) { || ... } click to toggle source

Yield each element to the block and return the result of the block when that result evaluates as true, terminating early like detect and find.

obj1 = Object.new
obj2 = Object.new

def obj1.foo?; false; end
def obj2.foo?; true ; end

def obj1.foo ; "foo1"; end
def obj2.foo ; "foo2"; end

[obj1, obj2].find_yield{ |obj| obj.foo if obj.foo? }  #=> "foo2"

Another example.

[1,2,3,4,5].find_yield{ |i| j = i+1; j if j % 4 == 0 }  #=> 4

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

[1,2,3].find_yield{ |_| false }    #=> nil
[false].find_yield(1){ |_| false } #=> 1
# File lib/core/facets/enumerable/find_yield.rb, line 28
def find_yield(fallback=nil) #:yield:
  each do |member|
    result = yield(member)
    return result if result 
  end
  fallback
end
Also aliased as: map_detect
frequency() click to toggle source

Generates a hash mapping each unique symbol in the array to the absolute frequency it appears.

[:a,:a,:b,:c,:c,:c].frequency  #=> {:a=>2,:b=>1,:c=>3}

CREDIT: Brian Schröder

# File lib/core/facets/enumerable/frequency.rb, line 18
def frequency
  p = Hash.new(0); each{ |v| p[v] += 1 }; p

end
graph(&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.graph{ |n| [n, n*n] }   # { 1=>1, 2=>4, 3=>9 }
sq_roots = numbers.graph{ |n| [n*n, n] }   # { 1=>1, 4=>2, 9=>3 }

CREDIT: Andrew Dudzik (adudzik), Trans

# File lib/core/facets/enumerable/graph.rb, line 12
def graph(&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,:graph)
  end
end
hashify(val=nil, &block) click to toggle source

The hashify methods is a higher-order message used to convert an enumerable object into a hash. Converting an enumerable object into a hash is not a one-to-one conversion, for this reason hashify is used to provide variant approches for the conversion most suited to the use case at hand. Here are some (but not a complete set of) examples.

If the enumerable is a collection of perfect pairs, like that which Hash#to_a generates, then assoc can be used.

a = [ [:a,1], [:b,2] ]
a.hashify.assoc  #=> { :a=>1, :b=>2 }

If it it contains only arrays, but are not perfect pairs, then concat can be used.

a = [ [:a,1,2], [:b,2], [:c], [:d] ]
a.hashify.concat  #=> { :a=>[1,2], :b=>[2], :c=>[], :d=>[] }

If the array contains objects other then arrays then the splat method might do the trick.

a = [ [:a,1,2], 2, :b, [:c,3], 9 ]
a.hashify.splat  #=> { [:a,1,2]=>2, :b=>[:c,3], 9=>nil }

Also, the particular dispatch can be left up the Hashify using the auto method. See Hashify#auto for details on this.

TODO: This method takes arguments only for the sake of the old method which has been deprecated. These will be removed eventually.

CREDIT: Robert Klemme, Sandor Szücs, Trans

# File lib/core/facets/enumerable/hashify.rb, line 40
def hashify(val=nil, &block)
  if val
    warn "The old Enumerable#hashify method has been be deprecated. Use #value_by instead."
    value_by{ val }
  end

  if block
    warn "The old Enumerable#hashify method has been be deprecated. Use #value_by instead."
    value_by(&block)
  end

  Hashifier.new(self)
end
hinge(init={}) { |h,v| ... } click to toggle source

Apply each element of an enumerable ot a hash by iterating over each element and yielding the hash and element.

[1,2,3].hinge{|h,e| h[e] = e+1 }
#=> {1=>2, 2=>3, 3=>4}

TODO: Enumerable#hinge will get a new name.

# File lib/core/facets/enumerable/hinge.rb, line 12
def hinge(init={})
  h = init
  each{ |v| yield(h,v) }
  h
end
incase?(what) click to toggle source

The same as include? but tested using === instead of ==.

[1, 2, "a"].incase?(2)       #=> true
[1, 2, "a"].incase?(String)  #=> true
[1, 2, "a"].incase?(3)       #=> false

Why the name ‘incase`? Because the method uses case-equality. Along with the alliteration for “in case” and the similarity with “include?”, it seemed like the perfect fit.

@author Lavir the Whiolet

# File lib/core/facets/enumerable/incase.rb, line 15
def incase?(what)
  any? { |x| what === x }
end
key_by() { |v| ... } click to toggle source

Convert enumerable into a Hash, iterating over each member where the provided block must return the key to by used to map to the value.

Examples:

[:a,:b,:c].key_by{ |v| v.to_s }
#=> {'a'=>:a, 'b'=>:b, 'c'=>:c}

TODO: How should this method behave with a Hash?

Returns: Hash

# File lib/core/facets/enumerable/key_by.rb, line 16
def key_by
  return to_enum(:key_by) unless block_given?

  h = {}
  each do |v|
    h[yield(v)] = v
  end

  return h
end
map_by() { || ... } click to toggle source

Like group_by, but maps the second value returned from the block.

a = [1,2,3,4,5]
a.map_by{ |e| [e % 2, e + 1] }
#=> { 0=>[3,5], 1=>[2,4,6] }

Works well with a hash too.

h = {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2}
h.map_by{ |k,v| [v, k.downcase] }
#=> {1=>["a", "b", "c"], 2=>["d", "e"]}

If a second value is not returned, map_by acts like group_by.

h = {"A"=>1, "B"=>1, "C"=>1, "D"=>2, "E"=>2}
h.map_by{ |k,v| v }
#=> {1=>[["A",1], ["B",1], ["C",1]], 2=>[["D",2], ["E",2]]}
# File lib/core/facets/enumerable/map_by.rb, line 21
def map_by #:yield:
  res = {}
  each do |a|
    k,v = yield(*a)
    if v
      (res[k] ||= []) << v
    else
      (res[k] ||= []) << a
    end
  end
  res
end
map_detect(fallback=nil)

Alias for find_yield.

DEPRECATE: This has been renamed to find_yield.

Alias for: find_yield
map_send(meth, *args, &block) click to toggle source

Send a message to each element and collect the result.

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

CREDIT: Sean O’Halpin

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

Map each element into another class via class constructor.

@param [#new] to_class

Generally a class, but any object that repsonds to #new.
# File lib/core/facets/enumerable/map_to.rb, line 8
def map_to(to_class)
  map{ |e| to_class.new(e) }
end
map_with(*arrays, &block) click to toggle source

Combines zip and map in a single efficient operation.

h = {}
[1,2,3].map_with [:x,:y,:z] do |n,k|
  h[k] = n
end
h  #=> {:x=>1, :y=>2, :z=>3}

@return [Hash]

@author Michael Kohl

# File lib/core/facets/enumerable/map_with.rb, line 14
def map_with(*arrays, &block)
  enum_for(:zip, *arrays).map(&block)
end
Also aliased as: zip_map
map_with_index() { |e, 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_with_index do |e, i|
    r << yield(e, 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 }

CREDIT: Andrew Dudzik (adudzik), Trans

# File lib/core/facets/enumerable/mash.rb, line 12
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,:graph)
  end
end
modulate(modulo) click to toggle source

Divide an array into groups by modulo of the index.

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

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

a = [1,1,2,3,3,4,5,5]

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

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

a.occur { |n| n == 1 }.sort   #=> [2,4]
a.occur { |n| n > 1 }.sort    #=> [1,3,5]
# File lib/core/facets/enumerable/occur.rb, line 18
def occur(n=nil) #:yield:
  result = Hash.new { |hash, key| hash[key] = Array.new }

  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
only() click to toggle source

Returns the only element in the enumerable. Raises an IndexError if the enumreable has more then one element.

[5].only      # => 5

expect IndexError do
  [1,2,3].only
end

expect IndexError do
  [].only
end

CREDIT: Lavir the Whiolet, Gavin Sinclair, Noah Gibbs

# File lib/core/facets/enumerable/only.rb, line 18
def only
  first = false
  first_item = nil

  each do |item|
    if first
      raise IndexError, "not the only element of enumerable"
    else
      first = true
      first_item = item
    end
  end
 
  if first
    return first_item
  else
    raise IndexError, "not the only element of enumerable"
  end
end
only?() click to toggle source

Does this Enumerable have the only element?

It differs from Enumerable#one? in that it does not check the items themselves. It checks the quantity only.

CREDIT: Lavir the Whiolet

# File lib/core/facets/enumerable/only.rb, line 45
def only?
  first = false

  each do |item|
    return false if first
    first = true
  end

  return first
end
organize_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}.organize_by {|x| x[0]}
#=> [ ['a'], ['is'], ['this', 'test'] ]

CREDIT: Erik Veenstra

# File lib/core/facets/enumerable/organize_by.rb, line 11
def organize_by(&b)
  group_by(&b).sort.transpose.pop || []   # group_by(&b).values ?
end
Also aliased as: cluster_by
pair(missing=nil) { |first, missing| ... } click to toggle source

Like ‘each_slice(2)` but ensures the last element has a pair if odd sized.

[:a,1,:b,2,:c,3].pair.to_a  #=> [[:a,1],[:b,2],[:c,3]]
# File lib/core/facets/enumerable/pair.rb, line 8
def pair(missing=nil)
  return to_enum(:pair) unless block_given?

  each_slice(2) do |kv|
    if kv.size == 1
      yield kv.first, missing
    else
      yield kv.first, kv.last
    end
  end
end
Also aliased as: associate
per(enum_method=nil, *enum_args) click to toggle source

Per element meta-functor.

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

Using fluid notation.

([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 21
def per(enum_method=nil, *enum_args)
  if enum_method
    Functor.new do |op, *args, &blk|
      __send__(enum_method || :map, *enum_args){ |x, *y| x.__send__(op, *y, *args, &blk) }
    end
  else
    Functor.new do |enumr_method, *enumr_args|
      Functor.new do |op, *args, &blk|
        __send__(enumr_method, *enumr_args){ |x, *y| x.__send__(op, *y, *args, &blk) }
      end
    end
  end
end
purge(*trash) { |e| ... } click to toggle source

A versitle compaction method. Like map but used to filter out multiple items in a single step.

Without trash arguments nil is assumed.

[1, nil, 2].purge  #=> [1,2]

If trash arguments are given, each argument is compared for a match using ==.

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

If a block is given, the yield is used in the matching condition instead of the element itsef.

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

NOTE: This could just as well be an override of the core compact method, but to avoid potential issues associated with overriding core methods we use the alternate name purge.

CREDIT: Trans

# File lib/core/facets/enumerable/purge.rb, line 27
def purge(*trash, &block)
  trash = [nil] if trash.empty?
  r = []
  if block_given?
    each do |e|
      y = yield(e)
      r << e unless trash.any?{|t| t == y}
    end
  else
    each do |e|
      r << e unless trash.any?{|t| t == e}
    end
  end
  r
end
recursively(*types, &block) click to toggle source

Returns a recursive functor, that allows enumerable methods to iterate through enumerable sub-elements. By default it only recurses over elements of the same type.

# File lib/core/facets/enumerable/recursively.rb, line 7
def recursively(*types, &block)
  Recursor.new(self, *types, &block)
end
squeeze(*limited_to) click to toggle source

Squeeze out the same elements. This behaves like C++ unique(), removing equivalent elements that are concomitant to each other. To get a similar result with Array#uniq, the array would have to be sorted first.

Calculation order is O(n).

Examples

[1,2,2,3,3,2,1].squeeze #=> [1,2,3,2,1]
[1,2,2,3,3,2,1].sort.squeeze #=> [1,2,3]
[1,2,2,3,3,2,1].squeeze(*[3]) #=> [1,2,2,3,2,1]

Returns [Array].

CREDIT: T. Yamada

# File lib/core/facets/enumerable/squeeze.rb, line 20
def squeeze(*limited_to)
  first = true
  r = []   # result
  c = nil  # current
  each do |e|
    if !limited_to.empty? && !limited_to.include?(e)
      r << e
    elsif first || c != e
      r << e
      first = false
      c = e
    end
  end
  r
end
sum(*identity, &block) click to toggle source

Uses + to sum the enumerated elements.

[1,2,3].sum      #=> 6
[3,3,3].sum      #=> 9

Note that Facets’ sum method is completely generic – it can work on any objects that respond to +.

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

For this reason it is usually a good idea to provide a default value. Consider the difference between the two expressions below.

[].sum           #=> nil
[].sum(0)        #=> 0

This default value also acts as an initial value.

[].sum(5)        #=> 5
[1,2,3].sum(10)  #=> 16

A block can also be passed to coax the elements before summation.

[1.1,2.2,3.3].sum(10.4, &:to_i)  #=> 16.4

Notice the initial value is not effected by the block.

@author Dawid Marcin Grzesiak

# File lib/core/facets/enumerable/sum.rb, line 32
def sum(*identity, &block)
  if block_given?
    map(&block).sum(*identity)
  else
    reduce(*identity) { |sum, element| sum + element }
  end
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/standard/facets/thread.rb, line 59
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 Enumerable#map_send but each iteration is processed via a separate thread.

CREDIT: Sean O’Halpin

# File lib/standard/facets/thread.rb, line 68
def threaded_map_send(meth, *args, &block)
  map{ |e| Thread.new(e){ |t| t.send(meth, *args, &block) } }.map{ |t| t.value }
end
unassociate(index = 1..-1) { |v| ... } click to toggle source

Take an associative array and unassociate it.

[[:a,1], [:b,2]].unassociate.to_a     #=> [:a, [1], :b, [2]]
[[:a,1], [:b,2]].unassociate(1).to_a  #=> [:a, 1, :b, 2]
# File lib/core/facets/enumerable/unassociate.rb, line 8
def unassociate(index = 1..-1)
  return to_enum(:unassociate, index) unless block_given?

  each do |v|
    case v
    when Array
      yield v[0]
      yield v[index]
    else
      yield v
      yield nil
    end
  end
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 }
#=> [-5, -4, -3, -2, -1, 0]
# File lib/core/facets/enumerable/uniq_by.rb, line 8
def uniq_by #:yield:
  h = {}; inject([]) {|a,x| h[yield(x)] ||= a << x}
end
value_by() { |item| ... } click to toggle source

Create a hash whose keys are the enumerable’s elements, with specified values.

If no block is given, the given parameter (default true) is used for all values, e.g.:

[1,2,3].value_by{ true }     #=> {1=>true, 2=>true, 3=>true}
[1,2,3].value_by{ "a" }      #=> {1=>"a", 2=>"a", 3=>"a"}

If a block is given, each key’s value is the result of running the block for that key, e.g.:

[1,2,3].value_by{ |n| "a"*n }  #=> {1=>"a", 2=>"aa", 3=>"aaa"}

@author Ronen Barzel

# File lib/core/facets/enumerable/value_by.rb, line 18
def value_by
  return to_enum(:value_by) unless block_given?

  h = {}
  each { |item| h[item] = yield(item) }
  h
end
visit(opts={}, &block) click to toggle source

Recursively iterate over all Enumerable elements, or subset given :type=>[type1, type2, …].

[1, 2, 8..9].visit{ |x| x.succ }
# => [2, 3, [9, 10]]
# File lib/core/facets/enumerable/visit.rb, line 9
def visit(opts={}, &block)
  type = opts[:type] ? [opts[:type]].flatten : [Enumerable]
  skip = opts[:skip]

  map do |v|
    case v
    when String # b/c of 1.8
      block.call(v)
    when *type
      v.visit(opts, &block)
    else
      if skip && Enumerable === v
        v
      else
        block.call(v)
      end
    end
  end
end
zip_map(*arrays, &block)
Alias for: map_with