Module: Enumerable
- Includes:
- Array::ToCSV
- Included in:
- Browser::Cache, Epi::Slop, Epi::Slop::Commands, ObjectSpace, Path, Trie
- Defined in:
- lib/epitools/core_ext/enumerable.rb
Instance Method Summary collapse
-
#*(other) ⇒ Object
Multiplies this Enumerable by something.
-
#**(n) ⇒ Object
Multiplies this Enumerable by itself ‘n` times.
-
#average ⇒ Object
Average the elements.
-
#blank? ⇒ Boolean
‘true’ if the Enumerable has no elements.
-
#combination(*args, &block) ⇒ Object
See: See Array#combination.
-
#counts ⇒ Object
(also: #count_by, #group_counts)
Counts how many instances of each object are in the collection, returning a hash.
-
#cross_product(other) ⇒ Object
(also: #cross)
Same behaviour as Enumerator#cross_product.
-
#foldl(methodname = nil, &block) ⇒ Object
Identical to “reduce” in ruby1.9 (or foldl in haskell.).
-
#group_neighbours_by(&block) ⇒ Object
(also: #group_neighbors_by)
Associative grouping; groups all elements who share something in common with each other.
-
#grouped_to_h ⇒ Object
(also: #group_to_h, #to_h_in_groups, #to_h_grouped)
Converts an array of 2-element key/value pairs into a Hash, grouped by key.
-
#groups ⇒ Object
(also: #grouped)
group_by the elements themselves.
-
#map_recursively(max_depth = nil, current_depth = 0, parent = nil, &block) ⇒ Object
(also: #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.
-
#parallel_map(num_workers = 8, &block) ⇒ Object
Map elements of this Enumerable in parallel using a pool full of Threads.
-
#permutation(*args, &block) ⇒ Object
See: Array#permutation.
-
#powerset ⇒ Object
Returns the powerset of the Enumerable.
- #reverse ⇒ Object
- #reverse_each ⇒ Object
-
#rle {|[count, last]| ... } ⇒ Object
run-length encode the array (returns an array of [count, element] pairs).
-
#rzip(other) ⇒ Object
Reverse zip (aligns the ends of two arrays, and zips them from right to left).
-
#select_recursively(max_depth = nil, current_depth = 0, parent = nil, &block) ⇒ Object
(also: #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.
-
#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.
-
#sort_numerically ⇒ Object
Sort strings by their numerical values.
-
#split_after(matcher = nil, options = {}, &block) ⇒ Object
Split the array into chunks, cutting between the matched element and the next element.
-
#split_at(matcher = nil, options = {}, &block) ⇒ Object
Split this enumerable into chunks, given some boundary condition.
-
#split_before(matcher = nil, options = {}, &block) ⇒ Object
Split the array into chunks, cutting before each matched element.
-
#split_between(&block) ⇒ Object
(also: #cut_between)
Split the array into chunks, cutting between two elements.
-
#sum(&block) ⇒ Object
(also: #sum_by)
Sum the elements.
-
#to_iter ⇒ Object
(also: #iter)
Convert the array into a stable iterator (Iter) object.
-
#uniq ⇒ Object
(also: #uniq_by)
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).
-
#unzip ⇒ Object
Does the opposite of #zip – converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ].
Methods included from Array::ToCSV
Instance Method Details
#*(other) ⇒ Object
Multiplies this Enumerable by something. (Same behaviour as Enumerator#*)
526 527 528 529 530 531 532 533 |
# 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 |
#**(n) ⇒ Object
Multiplies this Enumerable by itself ‘n` times.
538 539 540 |
# File 'lib/epitools/core_ext/enumerable.rb', line 538 def **(n) [self].cycle(n).reduce(:*) end |
#average ⇒ Object
Average the elements
231 232 233 234 235 236 237 238 |
# 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 |
#blank? ⇒ Boolean
‘true’ if the Enumerable has no elements
9 10 11 |
# File 'lib/epitools/core_ext/enumerable.rb', line 9 def blank? not any? end |
#combination(*args, &block) ⇒ Object
See: See Array#combination
366 367 368 |
# File 'lib/epitools/core_ext/enumerable.rb', line 366 def combination(*args, &block) to_a.combination(*args, &block) end |
#counts ⇒ Object 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
462 463 464 465 466 467 468 469 470 |
# 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 |
#cross_product(other) ⇒ Object Also known as: cross
Same behaviour as Enumerator#cross_product
545 546 547 |
# File 'lib/epitools/core_ext/enumerable.rb', line 545 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 }
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# 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 |
#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.)
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
# 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 |
#grouped_to_h ⇒ Object Also known as: group_to_h, to_h_in_groups, to_h_grouped
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.)
439 440 441 442 443 |
# 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 |
#groups ⇒ Object Also known as: grouped
group_by the elements themselves
477 478 479 |
# File 'lib/epitools/core_ext/enumerable.rb', line 477 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] ]
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# 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 |
#parallel_map(num_workers = 8, &block) ⇒ Object
Map elements of this Enumerable in parallel using a pool full of Threads
eg: repos.parallel_map { |repo| system “git pull #repo” }
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# 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 |
#permutation(*args, &block) ⇒ Object
See: Array#permutation
359 360 361 |
# File 'lib/epitools/core_ext/enumerable.rb', line 359 def permutation(*args, &block) to_a.permutation(*args, &block) end |
#powerset ⇒ Object
Returns the powerset of the Enumerable
Example:
[1,2].powerset #=> [[], [1], [2], [1, 2]]
376 377 378 379 380 381 382 383 |
# 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 |
#reverse ⇒ Object
46 47 48 |
# File 'lib/epitools/core_ext/enumerable.rb', line 46 def reverse to_a.reverse end |
#reverse_each ⇒ Object
55 56 57 |
# File 'lib/epitools/core_ext/enumerable.rb', line 55 def reverse_each to_a.reverse_each end |
#rle {|[count, last]| ... } ⇒ Object
run-length encode the array (returns an array of [count, element] pairs)
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
# 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 |
#rzip(other) ⇒ Object
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
394 395 396 |
# File 'lib/epitools/core_ext/enumerable.rb', line 394 def rzip(other) reverse_each.zip(other.reverse_each).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] ]
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# 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(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.
28 29 30 31 32 33 34 35 36 37 38 39 40 |
# 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_numerically ⇒ Object
Sort strings by their numerical values
511 512 513 514 515 516 517 518 519 520 521 |
# 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_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] ]
140 141 142 143 144 |
# File 'lib/epitools/core_ext/enumerable.rb', line 140 def split_after(matcher=nil, ={}, &block) [:after] ||= true [:include_boundary] ||= true split_at(matcher, , &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. ]
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 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/epitools/core_ext/enumerable.rb', line 83 def split_at(matcher=nil, ={}, &block) include_boundary = [: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 = [:once] == true ? 1 : [: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 [: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] ]
152 153 154 155 |
# File 'lib/epitools/core_ext/enumerable.rb', line 152 def split_before(matcher=nil, ={}, &block) [:include_boundary] ||= true split_at(matcher, , &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] ]
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# 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(&block) ⇒ Object Also known as: sum_by
Sum the elements
217 218 219 220 221 222 223 |
# File 'lib/epitools/core_ext/enumerable.rb', line 217 def sum(&block) if block_given? map(&block).reduce(:+) else reduce(:+) end end |
#to_iter ⇒ Object Also known as: iter
Convert the array into a stable iterator (Iter) object.
451 452 453 |
# File 'lib/epitools/core_ext/enumerable.rb', line 451 def to_iter Iter.new(to_a) end |
#uniq ⇒ Object Also known as: uniq_by
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)
245 246 247 248 249 250 251 252 |
# 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 |
#unzip ⇒ Object
Does the opposite of #zip – converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]
401 402 403 404 |
# 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 |