Module Enumerable
In: lib/core/facets/enumerable/value_by.rb
lib/core/facets/enumerable/every.rb
lib/core/facets/enumerable/frequency.rb
lib/core/facets/enumerable/modulate.rb
lib/core/facets/enumerable/hinge.rb
lib/core/facets/enumerable/visit.rb
lib/core/facets/enumerable/incase.rb
lib/core/facets/enumerable/purge.rb
lib/core/facets/enumerable/organize_by.rb
lib/core/facets/enumerable/only.rb
lib/core/facets/enumerable/mash.rb
lib/core/facets/enumerable/recursively.rb
lib/core/facets/enumerable/pair.rb
lib/core/facets/enumerable/map_by.rb
lib/core/facets/enumerable/exclude.rb
lib/core/facets/enumerable/filter.rb
lib/core/facets/enumerable/sum.rb
lib/core/facets/enumerable/map_with_index.rb
lib/core/facets/enumerable/uniq_by.rb
lib/core/facets/enumerable/occur.rb
lib/core/facets/enumerable/each_by.rb
lib/core/facets/enumerable/squeeze.rb
lib/core/facets/enumerable/hashify.rb
lib/core/facets/enumerable/expand.rb
lib/core/facets/enumerable/map_to.rb
lib/core/facets/enumerable/cluster.rb
lib/core/facets/enumerable/unassociate.rb
lib/core/facets/enumerable/map_send.rb
lib/core/facets/enumerable/associate.rb
lib/core/facets/enumerable/defer.rb
lib/core/facets/enumerable/compact_map.rb
lib/core/facets/enumerable/graph.rb
lib/core/facets/enumerable/apply.rb
lib/core/facets/enumerable/per.rb
lib/core/facets/enumerable/map_with.rb
lib/core/facets/enumerable/find_yield.rb
lib/core/facets/enumerable/accumulate.rb
lib/core/facets/enumerable/ewise.rb
lib/core/facets/enumerable/key_by.rb
lib/standard/facets/enumargs.rb
lib/standard/facets/thread.rb

Methods

Classes and Modules

Module Enumerable::Argumentable
Class Enumerable::Hashifier
Class Enumerable::Recursor

Constants

Arguments = Argumentable   DEPRECATED: Module alias for old name.

Public Instance methods

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

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

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

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

  r = [1,2,3].apply.+
  r  #=> 6
associate(missing=nil)

Alias for pair

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

cluster_by(&b)

Alias for organize_by

collect_with_index()

Alias for map_with_index

compact_collect(&block)

Alias for compact_map

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.

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.

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

elementwise(count=1)

Alias for ewise

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]

In place version of every.

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.

The inverse of include?.

 [:a, :b].exclude?(:c)  #=> true
 [:a, :b].exclude?(:a)  #=> false

Expand all elements of an Enumerable object.

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

CREDIT: Trans

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

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

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

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

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

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.

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

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

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]]}
map_detect(fallback=nil)

Alias for find_yield

Send a message to each element and collect the result.

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

CREDIT: Sean O‘Halpin

Map each element into another class via class constructor.

@param [new] to_class

  Generally a class, but any object that repsonds to #new.

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

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

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

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’?

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]

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

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

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

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

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]

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

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.

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

Uses #+ to sum the enumerated elements.

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

Note that Facetssum 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

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

CREDIT: Sean O‘Halpin

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

CREDIT: Sean O‘Halpin

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]

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]

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

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

  [1, 2, 8..9].visit{ |x| x.succ }
  # => [2, 3, [9, 10]]
zip_map(*arrays, &block)

Alias for map_with

[Validate]