Module | Enumerable

In:

lib/core/facets/enumerable/accumulate.rb
lib/core/facets/enumerable/cluster_by.rb
lib/core/facets/enumerable/compact_map.rb
lib/core/facets/enumerable/count.rb
lib/core/facets/enumerable/defer.rb
lib/core/facets/enumerable/each_by.rb
lib/core/facets/enumerable/each_with_object.rb
lib/core/facets/enumerable/every.rb
lib/core/facets/enumerable/ewise.rb
lib/core/facets/enumerable/exclude.rb
lib/core/facets/enumerable/expand.rb
lib/core/facets/enumerable/filter.rb
lib/core/facets/enumerable/find_yield.rb
lib/core/facets/enumerable/frequency.rb
lib/core/facets/enumerable/graph.rb
lib/core/facets/enumerable/group_by.rb
lib/core/facets/enumerable/map_by.rb
lib/core/facets/enumerable/map_send.rb
lib/core/facets/enumerable/map_with.rb
lib/core/facets/enumerable/map_with_index.rb
lib/core/facets/enumerable/modulate.rb
lib/core/facets/enumerable/none.rb
lib/core/facets/enumerable/occur.rb
lib/core/facets/enumerable/one.rb
lib/core/facets/enumerable/per.rb
lib/core/facets/enumerable/purge.rb
lib/core/facets/enumerable/recursively.rb
lib/core/facets/enumerable/sum.rb
lib/core/facets/enumerable/take.rb
lib/core/facets/enumerable/uniq_by.rb
lib/core/facets/enumerable/visit.rb
lib/core/facets/to_hash.rb
lib/core-uncommon/facets/enumerable/has.rb
lib/standard/facets/thread.rb
lib/supplemental/facets/enumargs.rb

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]

### Methods

accumulate accumulate_all cluster_by collect_with_index compact_collect compact_map count defer each_by each_with_object elementwise every every! ewise exclude? expand filter find_yield frequency graph group_by has? have? map_by map_detect map_send map_with map_with_index mash modulate none? occur one? per purge recursively sum take threaded_map threaded_map_send to_h to_h_assoc to_h_auto to_h_flat to_h_multi to_h_splat uniq_by visit zip_map

### Classes and Modules

Module Enumerable::Arguments
Class Enumerable::Recursor

### Public Instance methods

accumulate(iterations=1)

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

accumulate_all(iterations=1)

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

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

cluster_by(&b)

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]}
       #=> [ ['a'], ['is'], ['this', 'test'] ]

CREDIT: Erik Veenstra

collect_with_index()

Alias for map_with_index

compact_collect(&block)

Alias for compact_map

compact_map() {|*a| ...}

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.

count(*items, &block)

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

The method can count more than one item by supplying additional arguments.

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

A block may also be used to select countable entries.

      e.count{ |x| String === x }  #=> 2

Note that Hash#count only considers values.

      e = { 'a' => 2, 'x' => 2, 'b' => 1 }

      e.count(1)  #=> 1
      e.count(2)  #=> 2

NOTE: This is one of the few method overrides in Facets.

CREDIT: Trans

defer() {|output, *input| ...}

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.

each_by(steps=nil, &block)

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

each_with_object(memo) {|| ...}

A variation of inject that saves one from having to return the aggregate/memo argument.

Say we want to count characters in a string. Using the each_with_object method we have:

       "string".each_with_object(Hash.new(0)) do |c, h|
         h[c] += 1
       end

versus using inject which would be:

       "string".inject(Hash.new(0)) do |h, c|
         h[c] +=1
         h
       end

Notice that the order of the block parameters is reversed.

This method used be called injecting and had the same parameter order as inject, but Ruby 1.9 has adopted this method, so we support it instead.

elementwise(count=1)

Alias for ewise

every()

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]

every!()

In place version of every.

ewise(count=1)

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.

exclude?(object)

The inverse of include?.

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

expand()

Expand all elements of an Enumerable object.

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

CREDIT: Trans

filter(output=[]) {|output, *input| ...}

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

find_yield(fallback=nil) {|| ...}

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

frequency()

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

graph(&yld)

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

group_by( {|| ...}

Enumerable#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] }

Applied to an array.

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

Applied to a hash:

      {:a=>1, :b=>2, :c=>1}.group_by{ |k,v| v }
      #=> { 1=>[[:c,1], [:a,1]], 2=>[[:b,2]] }

CREDIT: Erik Veenstra

has?(what)

The same as include?() but equality is tested using ===.

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

NOTE: This is not (presently) a common core extension and is not loaded automatically when using `require ‘facets‘`.

TODO: Perhaps matches? would be a better name?

CREDIT: Lavir the Whiolet

have?(what)

Alias for has?

map_by( {|| ...}

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

map_send(meth, *args, &block)

Send a message to each element and collect the result.

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

CREDIT: Sean O‘Halpin

map_with(*arrays, &block)

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}

map_with_index() {|e, i| ...}

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

mash(&yld)

Alias for graph

modulate(modulo)

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

none?( {|e| ...}

Enumerable#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, Enumerable#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

occur(n=nil) {|| ...}

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]

one?( {|e| ...}

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

If no predicate is provided, Enumerable#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

per(enum_method=nil, *enum_args)

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]

purge(*trash) {|e| ...}

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

recursively(*types, &block)

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.

sum(identity = 0, &block)

Uses #+ to sum the enumerated elements.

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

take(n)

Return the first n items from the collection

threaded_map( {|| ...}

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

CREDIT: Sean O‘Halpin

threaded_map_send(meth, *args, &block)

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

CREDIT: Sean O‘Halpin

to_h(mode=nil)

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

CREDIT: Trans

to_h_assoc()
to_h_auto()
to_h_flat()
to_h_multi()
to_h_splat()
uniq_by( {|| ...}

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]

visit(opts={}, &block)

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]