module Enumerable
Public Instance Methods
Multiplies this Enumerable
by something. (Same behaviour as Enumerator#*
)
# File lib/epitools/core_ext/enumerable.rb, line 526 def *(other) case other when Integer, String to_enum * other when Enumerable to_enum.cross_product(other) end end
Multiplies this Enumerable
by itself ‘n` times.
# File lib/epitools/core_ext/enumerable.rb, line 538 def **(n) [self].cycle(n).reduce(:*) end
Average the elements
# File lib/epitools/core_ext/enumerable.rb, line 231 def average count = 0 sum = 0 each { |e| count += 1; sum += e } sum / count.to_f end
‘true’ if the Enumerable
has no elements
# File lib/epitools/core_ext/enumerable.rb, line 9 def blank? not any? end
See: See Array#combination
# File lib/epitools/core_ext/enumerable.rb, line 366 def combination(*args, &block) to_a.combination(*args, &block) end
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 #=> {:a=>1, :b=>1, :c=>4}
# File lib/epitools/core_ext/enumerable.rb, line 462 def counts h = Hash.of_integers if block_given? each { |x| h[yield x] += 1 } else each { |x| h[x] += 1 } end h end
Same behaviour as Enumerator#cross_product
# File lib/epitools/core_ext/enumerable.rb, line 545 def cross_product(other) to_enum.cross_product(other) end
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 }
# File lib/epitools/core_ext/enumerable.rb, line 323 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
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.)
# File lib/epitools/core_ext/enumerable.rb, line 416 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
Converts an array of 2-element key/value pairs into a Hash
, grouped by key. (Like to_h, but the pairs can have duplicate keys.)
# File lib/epitools/core_ext/enumerable.rb, line 439 def grouped_to_h result = Hash.of_arrays each {|k,v| result[k] << v } result end
group_by the elements themselves
# File lib/epitools/core_ext/enumerable.rb, line 477 def groups group_by(&:self) end
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] ]
# File lib/epitools/core_ext/enumerable.rb, line 264 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
Map elements of this Enumerable
in parallel using a pool full of Threads
eg: repos.parallel_map { |repo| system “git pull #{repo}” }
# File lib/epitools/core_ext/enumerable.rb, line 190 def parallel_map(num_workers=8, &block) require 'thread' queue = Queue.new each { |e| queue.push e } Enumerator.new(queue.size) do |y| workers = (0...num_workers).map do Thread.new do begin while e = queue.pop(true) y << block.call(e) end rescue ThreadError end end end workers.map(&:join) end end
See: Array#permutation
# File lib/epitools/core_ext/enumerable.rb, line 359 def permutation(*args, &block) to_a.permutation(*args, &block) end
Returns the powerset of the Enumerable
Example:
[1,2].powerset #=> [[], [1], [2], [1, 2]]
# File lib/epitools/core_ext/enumerable.rb, line 376 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
# File lib/epitools/core_ext/enumerable.rb, line 46 def reverse to_a.reverse end
# File lib/epitools/core_ext/enumerable.rb, line 55 def reverse_each to_a.reverse_each end
run-length encode the array (returns an array of [count, element] pairs)
# File lib/epitools/core_ext/enumerable.rb, line 485 def rle return to_enum(:rle) unless block_given? last = nil result = [] count = 1 each do |e| if last if last != e yield [count, last] count = 1 else count += 1 end end last = e end yield [count, last] end
Reverse zip (aligns the ends of two arrays, and zips them from right to left)
eg:
>> [5,39].rzip([:hours, :mins, :secs]) => [ [:mins, 5], [:secs, 39] ]
Note: Like zip, it will pad the second array if it’s shorter than the first
# File lib/epitools/core_ext/enumerable.rb, line 394 def rzip(other) reverse_each.zip(other.reverse_each).reverse_each end
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] ]
# File lib/epitools/core_ext/enumerable.rb, line 294 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 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.
# File lib/epitools/core_ext/enumerable.rb, line 28 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
Sort strings by their numerical values
# File lib/epitools/core_ext/enumerable.rb, line 511 def sort_numerically sort_by do |e| e = e.path if e.is_a? Path if e.is_a? String e.split(/(\d+)/).map { |s| s =~ /^\d+$/ ? s.to_i : s } else [e] end end end
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] ]
# File lib/epitools/core_ext/enumerable.rb, line 140 def split_after(matcher=nil, options={}, &block) options[:after] ||= true options[:include_boundary] ||= true split_at(matcher, options, &block) end
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. ]
# File lib/epitools/core_ext/enumerable.rb, line 83 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 the array into chunks, cutting before each matched element.
Example:
[1,2,3,4].split_before{|e| e == 3 } #=> [ [1,2], [3,4] ]
# File lib/epitools/core_ext/enumerable.rb, line 152 def split_before(matcher=nil, options={}, &block) options[:include_boundary] ||= true split_at(matcher, options, &block) end
Split the array into chunks, cutting between two elements.
Example:
[1,1,2,2].split_between{|a,b| a != b } #=> [ [1,1], [2,2] ]
# File lib/epitools/core_ext/enumerable.rb, line 163 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 the elements
# File lib/epitools/core_ext/enumerable.rb, line 217 def sum(&block) if block_given? map(&block).reduce(:+) else reduce(:+) end end
Convert the array into a stable iterator (Iter
) object.
# File lib/epitools/core_ext/enumerable.rb, line 451 def to_iter Iter.new(to_a) end
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)
# File lib/epitools/core_ext/enumerable.rb, line 245 def uniq already_seen = Set.new Enumerator::Lazy.new(self) do |yielder, value| key = block_given? ? yield(value) : value yielder << value if already_seen.add?(key) end end
Does the opposite of zip – converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]
# File lib/epitools/core_ext/enumerable.rb, line 401 def unzip # TODO: make it work for arrays containing uneven-length contents to_a.transpose end