Module: Enumerable

Included in:
Tuple
Defined in:
lib/standard/facets/thread.rb,
lib/standard/facets/enumargs.rb,
lib/core/facets/array/indexes.rb,
lib/core/facets/enumerable/per.rb,
lib/core/facets/enumerable/mash.rb,
lib/core/facets/enumerable/only.rb,
lib/core/facets/enumerable/pair.rb,
lib/core/facets/enumerable/apply.rb,
lib/core/facets/enumerable/defer.rb,
lib/core/facets/enumerable/every.rb,
lib/core/facets/enumerable/ewise.rb,
lib/core/facets/enumerable/graph.rb,
lib/core/facets/enumerable/hinge.rb,
lib/core/facets/enumerable/occur.rb,
lib/core/facets/enumerable/purge.rb,
lib/core/facets/enumerable/visit.rb,
lib/core/facets/enumerable/expand.rb,
lib/core/facets/enumerable/incase.rb,
lib/core/facets/enumerable/key_by.rb,
lib/core/facets/enumerable/map_by.rb,
lib/core/facets/enumerable/map_to.rb,
lib/core/facets/enumerable/cluster.rb,
lib/core/facets/enumerable/each_by.rb,
lib/core/facets/enumerable/exclude.rb,
lib/core/facets/enumerable/hashify.rb,
lib/core/facets/enumerable/squeeze.rb,
lib/core/facets/enumerable/uniq_by.rb,
lib/core/facets/enumerable/map_send.rb,
lib/core/facets/enumerable/map_with.rb,
lib/core/facets/enumerable/modulate.rb,
lib/core/facets/enumerable/value_by.rb,
lib/core/facets/enumerable/associate.rb,
lib/core/facets/enumerable/frequency.rb,
lib/core/facets/enumerable/accumulate.rb,
lib/core/facets/enumerable/find_yield.rb,
lib/core/facets/enumerable/compact_map.rb,
lib/core/facets/enumerable/organize_by.rb,
lib/core/facets/enumerable/recursively.rb,
lib/core/facets/enumerable/unassociate.rb,
lib/core/facets/enumerable/with_position.rb,
lib/core/facets/enumerable/map_with_index.rb

Defined Under Namespace

Modules: Argumentable Classes: Hashifier, Position, Recursor

Constant Summary collapse

Arguments =

DEPRECATED: Module alias for old name.

Argumentable

Instance Method Summary collapse

Instance Method Details

#accumulate(iterations = 1) ⇒ Object

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



42
43
44
45
46
47
48
49
50
# 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) ⇒ Object

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

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


56
57
58
59
60
61
62
63
64
# 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

#applyObject

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

r = [1,2,3].apply.+ 
r  #=> 6


12
13
14
15
16
17
# 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

#clusterObject

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


13
14
15
16
17
18
19
20
21
22
23
# 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

#compact_map(&block) ⇒ Object Also known as: compact_collect

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.



17
18
19
20
# File 'lib/core/facets/enumerable/compact_map.rb', line 17

def compact_map(&block)
  warn "Enumerable#compact_map is deprecated. Use Enumerable#filter_map instead.", uplevel: 1
  filter_map(&block)
end

#defer(&blk) ⇒ Object

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.



29
30
31
32
# File 'lib/core/facets/enumerable/defer.rb', line 29

def defer(&blk)
  warn "Enumerable#defer is deprecated. Use Enumerable#lazy instead.", uplevel: 1
  lazy
end

#each_by(steps = nil, &block) ⇒ Object

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



20
21
22
23
24
25
26
27
28
# File 'lib/core/facets/enumerable/each_by.rb', line 20

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

#everyObject

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]


11
12
13
# File 'lib/core/facets/enumerable/every.rb', line 11

def every
  per(:map)
end

#every!Object

In place version of #every.

Raises:

  • (NoMethodError)


17
18
19
20
# File 'lib/core/facets/enumerable/every.rb', line 17

def every!
  raise NoMethodError unless respond_to?(:map!)
  per(:map!)
end

#ewise(count = 1) ⇒ Object Also known as: elementwise

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.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 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

#exclude?(object) ⇒ Boolean

The inverse of #include?.

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

Returns:

  • (Boolean)


10
11
12
# File 'lib/core/facets/enumerable/exclude.rb', line 10

def exclude?(object)
  !include?(object)
end

#expandObject

Expand all elements of an Enumerable object.

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

CREDIT: Trans



8
9
10
11
12
# File 'lib/core/facets/enumerable/expand.rb', line 8

def expand
  map do |x|
   (Enumerable === x ? x.expand : x)
  end
end

#find_yield(fallback = nil) ⇒ Object Also known as: map_detect

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


28
29
30
31
32
33
34
# 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

#frequencyObject

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

-- NOTE: So why not use #inject here? e.g. ...

inject(Hash.new(0)){|p,v| p[v]+=1; p}

Because it is a fair bit slower than the traditional definition. ++



18
19
20
21
# File 'lib/core/facets/enumerable/frequency.rb', line 18

def frequency
  warn "Enumerable#frequency is deprecated. Use Enumerable#tally instead.", uplevel: 1
  tally
end

#graph(&yld) ⇒ Object

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



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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) ⇒ Object

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



40
41
42
43
44
45
46
47
48
49
50
51
52
# 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 = {}, &block) ⇒ Object

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.



12
13
14
15
# File 'lib/core/facets/enumerable/hinge.rb', line 12

def hinge(init={}, &block)
  warn "Enumerable#hinge is deprecated. Use Enumerable#each_with_object instead (note: block args are reversed).", uplevel: 1
  each_with_object(init) { |e, obj| block.call(obj, e) }
end

#incase?(what) ⇒ Boolean

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.

Returns:

  • (Boolean)


15
16
17
# File 'lib/core/facets/enumerable/incase.rb', line 15

def incase?(what)
  any? { |x| what === x }
end

#indexes(*args) ⇒ Object Also known as: index_all

Returns an enumerator of indexes of all objects in receiver such that the object is == to obj.

If a block is given instead of an argument, returns the indexes of all objects for which the block returns true.

If neither a block nor argument is given, an Enumerator for all indexes is returned.

Returns [] if no match is found.

a = ("a".."c") a.indexes("b").to_a #=> [1] a.indexes("z").to_a #=> [] a.indexes { |x| x == "b" }.to_a #=> [1]

Like Array#index/rindex and Enumerable#find_index but returns all indexes instead of just the first/last.

See also: proposal to add Array#indexes to Ruby language: https://bugs.ruby-lang.org/issues/6596



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/core/facets/array/indexes.rb', line 62

def indexes(*args)
  # Enumerable doesn't have each_index like Array has, so this uses each_with_index instead.
  case args.length
  when 0
    if block_given?
      each_with_index.select {|el, i| yield(el)     }.map {|el, i| i }
    else
      each_with_index.                                map {|el, i| i }.to_enum
    end
  when 1
    other = args.first
    each_with_index.  select {|el, i| el == other   }.map {|el, i| i }
  else
    raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)"
  end
end

#key_byObject

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



16
17
18
19
20
21
22
23
24
25
# 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_byObject

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


21
22
23
24
25
26
27
28
29
30
31
32
# 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_send(meth, *args, &block) ⇒ Object

Send a message to each element and collect the result.

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

CREDIT: Sean O'Halpin



9
10
11
# 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) ⇒ Object

Map each element into another class via class constructor.

Parameters:

  • to_class (#new)

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



8
9
10
# 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) ⇒ Hash Also known as: zip_map

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}

Returns:



14
15
16
# File 'lib/core/facets/enumerable/map_with.rb', line 14

def map_with(*arrays, &block)
  enum_for(:zip, *arrays).map(&block)
end

#map_with_index(&block) ⇒ Object Also known as: collect_with_index

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



10
11
12
13
# File 'lib/core/facets/enumerable/map_with_index.rb', line 10

def map_with_index(&block)
  warn "Enumerable#map_with_index is deprecated. Use Enumerable#map.with_index instead.", uplevel: 1
  map.with_index(&block)
end

#mash(&yld) ⇒ Object

Deprecated.

Use Enumerable#graph instead.



6
7
8
9
# File 'lib/core/facets/enumerable/mash.rb', line 6

def mash(&yld)
  warn "Enumerable#mash is deprecated. Use Enumerable#graph instead.", uplevel: 1
  graph(&yld)
end

#modulate(modulo) ⇒ Object

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

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
# 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) ⇒ Object

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]


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 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

#onlyObject

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



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 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?Boolean

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

Returns:

  • (Boolean)


45
46
47
48
49
50
51
52
53
54
# 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) ⇒ Object Also known as: cluster_by

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



11
12
13
# 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

#pair(missing = nil) ⇒ Object Also known as: associate

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


8
9
10
11
12
13
14
15
16
17
18
# 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

#per(enum_method = nil, *enum_args) ⇒ Object

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]


21
22
23
24
25
26
27
28
29
30
31
32
33
# 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, &block) ⇒ Object

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



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 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) ⇒ Object

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.



7
8
9
# File 'lib/core/facets/enumerable/recursively.rb', line 7

def recursively(*types, &block)
  Recursor.new(self, *types, &block)
end

#squeeze(*limited_to) ⇒ Object

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



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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

#threaded_mapObject

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

CREDIT: Sean O'Halpin



59
60
61
# 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) ⇒ Object

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

CREDIT: Sean O'Halpin



68
69
70
# 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)) ⇒ Object

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]


8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 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(&block) ⇒ Object

Deprecated.

Use Enumerable#uniq with a block instead (Ruby 1.9.2+).



4
5
6
7
# File 'lib/core/facets/enumerable/uniq_by.rb', line 4

def uniq_by(&block)
  warn "Enumerable#uniq_by is deprecated. Use Enumerable#uniq(&block) instead.", uplevel: 1
  uniq(&block)
end

#value_byObject

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"}


18
19
20
21
22
23
24
# 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) ⇒ Object

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

[1, 2, 8..9].visit{ |x| x.succ }
# => [2, 3, [9, 10]]


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 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

#with_positionObject

Allows one to know the position of the enumeration easily.

5.times.collect.with_position do |i, position|
   $stdout.write "#{i}#{position.middle? ? ',' : ''}"
end

Where this becomes more useful, is when it's easy to determine the position of elements, e.g.

10.times.select(&:odd?).with_position{|n,p| ... }


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/core/facets/enumerable/with_position.rb', line 40

def with_position
	return to_enum(:with_position) unless block_given?
	
	position = Position.new
	
	while item = self.next
		begin
			self.peek
		rescue StopIteration
			position.at_end!
		end
		
		yield item, position.dup
		
		position.after_first!
	end
rescue StopIteration
end