Module: Enumerable

Included in:
Browser::Cache, ObjectSpace, Trie
Defined in:
lib/epitools/core_ext/enumerable.rb

Instance Method Summary collapse

Instance Method Details

#*(other) ⇒ Object

Multiplies this Enumerable by something. (Same behaviour as Enumerator#*)



411
412
413
414
415
416
417
418
# File 'lib/epitools/core_ext/enumerable.rb', line 411

def *(other)
  case other
  when Integer, String
    to_enum * other
  when Enumerable
    to_enum.cross_product(other)
  end
end

#**(n) ⇒ Object

Multiplies this Enumerable by itself ‘n` times.



423
424
425
# File 'lib/epitools/core_ext/enumerable.rb', line 423

def **(n)
  [self].cycle(n).reduce(:*)
end

#averageObject

Average the elements



187
188
189
190
191
192
193
194
# File 'lib/epitools/core_ext/enumerable.rb', line 187

def average
  count = 0
  sum   = 0

  each { |e| count += 1; sum += e }

  sum / count.to_f
end

#blank?Boolean

‘true’ if the Enumerable has no elements

Returns:

  • (Boolean)


6
7
8
# File 'lib/epitools/core_ext/enumerable.rb', line 6

def blank?
  not any?
end

#combination(*args, &block) ⇒ Object

See: See Array#combination



319
320
321
# File 'lib/epitools/core_ext/enumerable.rb', line 319

def combination(*args, &block)
  to_a.combination(*args, &block)
end

#countsObject Also known as: count_by, group_counts

Counts how many instances of each object are in the collection, returning a hash. (Also optionally takes a block.)

eg: [:a, :b, :c, :c, :c, :c].counts #=> :b=>1, :c=>4



388
389
390
391
392
393
394
395
396
# File 'lib/epitools/core_ext/enumerable.rb', line 388

def counts
  h = Hash.of_integers
  if block_given?
    each { |x| h[yield x] += 1 }
  else
    each { |x| h[x] += 1 }
  end
  h
end

#cross_product(other) ⇒ Object Also known as: cross

Same behaviour as Enumerator#cross_product



430
431
432
# File 'lib/epitools/core_ext/enumerable.rb', line 430

def cross_product(other)
  to_enum.cross_product(other)
end

#foldl(methodname = nil, &block) ⇒ Object

Identical to “reduce” in ruby1.9 (or foldl in haskell.)

Example:

array.foldl{|a,b| a + b } == array[1..-1].inject(array[0]){|a,b| a + b }


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/epitools/core_ext/enumerable.rb', line 276

def foldl(methodname=nil, &block)
  result = nil

  raise "Error: pass a parameter OR a block, not both!" unless !!methodname ^ block_given?

  if methodname

    each_with_index do |e,i|
      if i == 0
        result = e
        next
      end

      result = result.send(methodname, e)
    end

  else

    each_with_index do |e,i|
      if i == 0
        result = e
        next
      end

      result = block.call(result, e)
    end

  end

  result
end

#group_neighbours_by(&block) ⇒ Object Also known as: group_neighbors_by

Associative grouping; groups all elements who share something in common with each other. You supply a block which takes two elements, and have it return true if they are “neighbours” (eg: belong in the same group).

Example:

[1,2,5,6].group_neighbours_by { |a,b| b-a <= 1 } #=> [ [1,2], [5,6] ]

(Note: This is a very fast one-pass algorithm – therefore, the groups must be pre-sorted.)



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/epitools/core_ext/enumerable.rb', line 356

def group_neighbours_by(&block)
  result = []
  cluster = [first]
  each_cons(2) do |a,b|
    if yield(a,b)
      cluster << b
    else
      result << cluster
      cluster = [b]
    end
  end

  result << cluster if cluster.any?

  result
end

#groupsObject Also known as: grouped

group_by the elements themselves



403
404
405
# File 'lib/epitools/core_ext/enumerable.rb', line 403

def groups
  group_by(&:self)
end

#map_recursively(max_depth = nil, current_depth = 0, parent = nil, &block) ⇒ Object Also known as: deep_map, recursive_map, map_recursive

The same as “map”, except that if an element is an Array or Enumerable, map is called recursively on that element. (Hashes are ignored because of the complications of block arguments and return values.)

Example:

[ [1,2], [3,4] ].deep_map{|e| e ** 2 } #=> [ [1,4], [9,16] ]


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/epitools/core_ext/enumerable.rb', line 217

def map_recursively(max_depth=nil, current_depth=0, parent=nil, &block)
  return self if max_depth and (current_depth > max_depth)

  map do |obj|
    if obj == parent # infinite loop scenario!
      yield obj
    else
      case obj
      when String, Hash
        yield obj
      when Enumerable
        obj.map_recursively(max_depth, current_depth+1, self, &block)
      else
        yield obj
      end
    end
  end
end

#permutation(*args, &block) ⇒ Object

See: Array#permutation



312
313
314
# File 'lib/epitools/core_ext/enumerable.rb', line 312

def permutation(*args, &block)
  to_a.permutation(*args, &block)
end

#powersetObject

Returns the powerset of the Enumerable

Example:

[1,2].powerset #=> [[], [1], [2], [1, 2]]


329
330
331
332
333
334
335
336
# File 'lib/epitools/core_ext/enumerable.rb', line 329

def powerset
  return to_enum(:powerset) unless block_given?
  a = to_a
  (0...2**a.size).each do |bitmask|
    # the bit pattern of the numbers from 0..2^(elements)-1 can be used to select the elements of the set...
    yield a.select.with_index { |e, i| bitmask[i] == 1 }
  end
end

#reverse_eachObject



43
44
45
# File 'lib/epitools/core_ext/enumerable.rb', line 43

def reverse_each
  to_a.to_enum(:reverse_each)
end

#select_recursively(max_depth = nil, current_depth = 0, parent = nil, &block) ⇒ Object Also known as: deep_select, recursive_select, select_recursive

The same as “select”, except that if an element is an Array or Enumerable, select is called recursively on that element.

Example:

[ [1,2], [3,4] ].select_recursively{|e| e % 2 == 0 } #=> [ [2], [4] ]


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/epitools/core_ext/enumerable.rb', line 247

def select_recursively(max_depth=nil, current_depth=0, parent=nil, &block)
  return self if max_depth and (current_depth > max_depth)

  map do |obj|
    if obj == parent # infinite loop scenario!
      obj if yield obj
    else
      case obj
      when String, Hash
        obj if yield obj
      when Enumerable
        obj.deep_select(max_depth, current_depth+1, self, &block)
      else
        obj if yield obj
      end
    end
  end.compact
end

#skip(n) ⇒ Object

Skip the first n elements and return an Enumerator for the rest, or pass them in succession to the block, if given. This is like “drop”, but returns an enumerator instead of converting the whole thing to an array.



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/epitools/core_ext/enumerable.rb', line 25

def skip(n)
  if block_given?
    each do |x|
      if n > 0
        n -= 1
      else
        yield x
      end
    end
  else
    to_enum(:skip, n)
  end
end

#split_after(matcher = nil, options = {}, &block) ⇒ Object

Split the array into chunks, cutting between the matched element and the next element.

Example:

[1,2,3,4].split_after{|e| e == 3 } #=> [ [1,2,3], [4] ]


128
129
130
131
132
# File 'lib/epitools/core_ext/enumerable.rb', line 128

def split_after(matcher=nil, options={}, &block)
  options[:after]             ||= true
  options[:include_boundary]  ||= true
  split_at(matcher, options, &block)
end

#split_at(matcher = nil, options = {}, &block) ⇒ Object

Split this enumerable into chunks, given some boundary condition. (Returns an array of arrays.)

Options:

:include_boundary => true  #=> include the element that you're splitting at in the results
                               (default: false)
:after => true             #=> split after the matched element (only has an effect when used with :include_boundary)
                               (default: false)
:once => flase             #=> only perform one split (default: false)

Examples:

[1,2,3,4,5].split{ |e| e == 3 }
#=> [ [1,2], [4,5] ]

"hello\n\nthere\n".each_line.split_at("\n").to_a
#=> [ ["hello\n"], ["there\n"] ]

[1,2,3,4,5].split(:include_boundary=>true) { |e| e == 3 }
#=> [ [1,2], [3,4,5] ]

chapters = File.read("ebook.txt").split(/Chapter \d+/, :include_boundary=>true)
#=> [ ["Chapter 1", ...], ["Chapter 2", ...], etc. ]


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/epitools/core_ext/enumerable.rb', line 71

def split_at(matcher=nil, options={}, &block)
  include_boundary = options[:include_boundary] || false

  if matcher.nil?
    boundary_test_proc = block
  else
    if matcher.is_a? Regexp
      boundary_test_proc = proc { |element| element =~ matcher }
    else
      boundary_test_proc = proc { |element| element == matcher }
    end
  end

  Enumerator.new do |yielder|
    current_chunk = []
    splits        = 0
    max_splits    = options[:once] == true ? 1 : options[:max_splits]

    each do |e|

      if boundary_test_proc.call(e) and (max_splits == nil or splits < max_splits)

        if current_chunk.empty? and not include_boundary
          next # hit 2 boundaries in a row... just keep moving, people!
        end

        if options[:after]
          # split after boundary
          current_chunk << e        if include_boundary   # include the boundary, if necessary
          yielder << current_chunk                         # shift everything after the boundary into the resultset
          current_chunk = []                              # start a new result
        else
          # split before boundary
          yielder << current_chunk                         # shift before the boundary into the resultset
          current_chunk = []                              # start a new result
          current_chunk << e        if include_boundary   # include the boundary, if necessary
        end

        splits += 1

      else
        current_chunk << e
      end

    end

    yielder << current_chunk if current_chunk.any?

  end
end

#split_before(matcher = nil, options = {}, &block) ⇒ Object

Split the array into chunks, cutting before each matched element.

Example:

[1,2,3,4].split_before{|e| e == 3 } #=> [ [1,2], [3,4] ]


140
141
142
143
# File 'lib/epitools/core_ext/enumerable.rb', line 140

def split_before(matcher=nil, options={}, &block)
  options[:include_boundary]  ||= true
  split_at(matcher, options, &block)
end

#split_between(&block) ⇒ Object Also known as: cut_between

Split the array into chunks, cutting between two elements.

Example:

[1,1,2,2].split_between{|a,b| a != b } #=> [ [1,1], [2,2] ]


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/epitools/core_ext/enumerable.rb', line 151

def split_between(&block)
  Enumerator.new do |yielder|
    current = []
    last    = nil

    each_cons(2) do |a,b|
      current << a
      if yield(a,b)
        yielder << current
        current = []
      end
      last = b
    end

    current << last unless last.nil?
    yielder << current
  end
end

#sum(&block) ⇒ Object Also known as: sum_by

Sum the elements



175
176
177
178
179
180
181
# File 'lib/epitools/core_ext/enumerable.rb', line 175

def sum(&block)
  if block_given?
    map(&block).reduce(:+)
  else
    reduce(:+)
  end
end

#to_iterObject Also known as: iter

Convert the array into a stable iterator (Iter) object.



377
378
379
# File 'lib/epitools/core_ext/enumerable.rb', line 377

def to_iter
  Iter.new(to_a)
end

#uniqObject

Lazily enumerate unique elements (WARNING: This can cause an infinite loop if you enumerate over a cycle,

since it will keep reading the input until it finds a unique element)


201
202
203
204
205
206
207
# File 'lib/epitools/core_ext/enumerable.rb', line 201

def uniq
  already_seen = Set.new

  Enumerator::Lazy.new(self) do |yielder, value|
    yielder << value if already_seen.add?(value)
  end
end

#unzipObject

Does the opposite of #zip – converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]



341
342
343
344
# File 'lib/epitools/core_ext/enumerable.rb', line 341

def unzip
  # TODO: make it work for arrays containing uneven-length contents
  to_a.transpose
end