This is a simple reimplementation of the core Enumerable module to allow the methods to take and pass-on arbitrary arguments to the underlying # 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 # and # do NOT work this way, but simply work as they do in Enumerable. The method # (and #) 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]
Accumulate a set of a set.
For example, in an ORM design if Group has_many User then
groups.accumulate.users
will return a list of users from all groups.
CREDIT: George Moshchovitis
# File lib/core/facets/enumerable/accumulate.rb, line 16 16: def accumulate 17: @_accumulate ||= Functor.new do |op, *args| 18: inject([]) { |a, x| a << x.send(op, *args) }.flatten 19: end 20: end
Similar to # but returns an array of the groups. Returned elements are sorted by block.
%w{this is a test}.cluster_by {|x| x[0]}
produces
[ ['a'], ['is'], ['this', 'test'] ]
CREDIT: Erik Veenstra
# File lib/core/facets/enumerable/cluster_by.rb, line 16 16: def cluster_by(&b) 17: group_by(&b).sort.transpose.pop || [] # group_by(&b).values ? 18: end
Returns all items that are equal in terms of the supplied block. If no block is given objects are considered to be equal if they return the same value for Object#hash and if obj1 == obj2.
[1, 2, 2, 3, 4, 4].commonality # => { 2 => [2, 2], 4 => [4, 4] } ["foo", "bar", "a"].commonality { |str| str.length } # => { 3 => ["foo, "bar"] } # Returns all persons that share their last name with another person. persons.collisions { |person| person.last_name }
CREDIT: Florian Gross
# File lib/core/facets/enumerable/commonality.rb, line 17 17: def commonality(&block) 18: had_no_block = !block 19: block ||= lambda { |item| item } 20: result = Hash.new { |hash, key| hash[key] = Array.new } 21: self.each do |item| 22: key = block.call(item) 23: result[key] << item 24: end 25: result.reject! do |key, values| 26: values.size <= 1 27: end 28: #return had_no_block ? result.values.flatten : result 29: return result 30: end
A more versitle # method. It can be used to collect and filter items out in one single step.
[1,2,3].compact_map do |n| n < 1 ? nil : n end
produces
[2,3]
CREDIT: Trans
DEPRECATE: This method should probably be removed b/c # does almost the same thing and enum.map{}.compact works too.
# File lib/core/facets/enumerable/compact_map.rb, line 19 19: def compact_map(&block) 20: y = [] 21: if block_given? 22: each do |*a| 23: r = yield(*a) 24: y << r unless r.nil? 25: end 26: else 27: each do |r| 28: y << r unless r.nil? 29: end 30: end 31: y 32: end
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 e.count('a', 1) #=> 3
Note that Hash#count only considers values.
e = { 'a' => 2, 'x' => 2, 'b' => 1 } e.count(1) #=> 1 e.count(2) #=> 2
CREDIT: Trans
# File lib/core/facets/enumerable/count.rb, line 23 23: def count(*items, &block) 24: if block || !items.empty? 25: r = self 26: r = r.select(&block) if block 27: r = r.select{ |x| items.any?{ |i| i == x } } if !items.empty? 28: r.size 29: else 30: begin 31: size 32: rescue 33: i=0; each{ |e| i+=1 }; i 34: end 35: end 36: end
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). each { |i| puts i }
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.
# File lib/core/facets/enumerable/defer.rb, line 30 30: def defer(&blk) 31: if block_given? 32: Denumerator.new do |output| 33: each do |*input| 34: yield(output, *input) 35: end 36: end 37: else 38: Denumerator.new do |output| 39: each do |*input| 40: output.yield(*input) 41: end 42: end 43: end 44: end
Divide on matching pattern.
['a1','b1','a2','b2'].divide(/^a/) => [['a1,'b1'],['a2','b2']]
CREDIT: Trans
# File lib/core/facets/enumerable/divide.rb, line 10 10: def divide(pattern) 11: memo = [] 12: each do |obj| 13: memo.push [] if pattern === obj 14: memo.last << obj 15: end 16: memo 17: end
Return list of dulicate elements.
CREDIT: Thibaut Barrère
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
# File lib/core/facets/enumerable/each_by.rb, line 22 22: def each_by(steps=nil, &block) 23: if steps 24: each_slice(steps, &block) 25: else 26: steps = block.arity.abs 27: each_slice(steps, &block) 28: #each_slice(steps) {|i| block.call(*i)} 29: end 30: end
A variation of # that saves one from having to return the aggregate/memo argument.
Say we want to count characters in a string. Using the # method we have:
string.each_with_object(Hash.new(0)) do |c, h| h[c] += 1 end
versus using # 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 # and had the same parameter order as #, but Ruby 1.9 has adopted this method, so we support it instead.
# File lib/core/facets/enumerable/each_with_object.rb, line 28 28: def each_with_object(memo) #:yield: 29: each do |element| 30: yield(element, memo) 31: end 32: memo 33: end
Long-term for #.
a = [1,2] a.elementwise + 3 #=> [4,5] a.elementwise + [4,5] #=> [5,7] a.elementwise + [[4,5],3] #=> [[5,7],[4,5]
Shannon’s entropy for an array - returns the average bits per symbol required to encode the array. Lower values mean less “entropy” - i.e. less unique information in the array.
%w{ a b c d e e e }.entropy #=>
CREDIT: Derek
# File lib/core/facets/enumerable/entropy.rb, line 14 14: def entropy 15: arr = to_a 16: probHash = arr.probability 17: # h is the Shannon entropy of the array 18: h = 1.to_f * probHash.keys.inject(0.to_f) do |sum, i| 19: sum + (probHash[i] * (Math.log(probHash[i])/Math.log(2.to_f))) 20: end 21: h 22: end
Returns an elemental object. This allows you to map a method on to every element.
r = [1,2,3].every + 3 #=> [4,5,6]
# File lib/core/facets/enumerable/every.rb, line 10 10: def every 11: per(:map) 12: end
In place version of #.
# File lib/core/facets/enumerable/every.rb, line 16 16: def every! 17: raise NoMethodError unless respond_to?(:map!) 18: per(:map!) 19: end
Returns an elementwise Functor designed to make R-like elementwise operations possible. This is very much like the # method, but it treats array argument specially.
[1,2].ewise + 3 #=> [4,5] [1,2].ewise + [4,5] #=> [5,7] [1,2].ewise + [[4,5],3] #=> [[5,7],[4,5]
Special thanks to Martin DeMello for helping to develop this.
# File lib/core/facets/enumerable/ewise.rb, line 17 17: def ewise(count=1) 18: EWISE[[self,count]] ||= Functor.new do |op,*args| 19: if args.empty? 20: r = self 21: count.times do 22: r = r.collect{ |a| a.send(op) } 23: end 24: r 25: else 26: r = args.collect do |arg| 27: if Array === arg #arg.kind_of?(Enumerable) 28: x = self 29: count.times do 30: ln = (arg.length > length ? length : arg.length ) 31: x = x.slice(0...ln).zip(arg[0...ln]).collect{ |a,b| a.send(op,b) } 32: #slice(0...ln).zip(arg[0...1n]).collect{ |a,b| b ? a.send(op,b) : nil } 33: end 34: x 35: else 36: x = self 37: count.times do 38: x = x.collect{ |a| a.send(op,arg) } 39: end 40: x 41: end 42: end 43: r.flatten! if args.length == 1 44: r 45: end 46: end 47: end
The inverse of #.
# File lib/core/facets/enumerable/exclude.rb, line 7 7: def exclude?(object) 8: !include?(object) 9: end
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.
(1..1_000_000_000). filter{ |out,i| out << i if i % 2 == 0 }. # like select filter{ |out,i| out << i + 100 }. # like map take(10).each{ |i| puts i }
This is very similar to #, but # handles argument better by reversing their order and using the splat operator.
# File lib/core/facets/enumerable/filter.rb, line 17 17: def filter(output=[]) #:yeild: 18: if block_given? 19: each do |*input| 20: yield(output, *input) 21: end 22: output 23: else 24: to_enum(:filter) 25: end 26: end
Yield each element to the block and return the result of the block when that result evaluates as true, terminating early like # and #.
obj1.foo? #=> false obj2.foo? #=> true obj2.foo #=> "value" [obj1, obj2].find_yield{ |obj| obj.foo if obj.foo? } #=> "value"
Another example:
[1,2,3,4,5].find_yield{ |i| j = i+1; j if j % 4 == 0 } #=> "5"
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
# File lib/core/facets/enumerable/find_yield.rb, line 23 23: def find_yield(fallback=nil) #:yield: 24: each do |member| 25: result = yield(member) 26: return result if result 27: end 28: fallback 29: end
Generates a hash mapping each unique symbol in the array to the absolute frequency it appears.
CREDIT: Brian Schröder
# File lib/core/facets/enumerable/frequency.rb, line 8 8: def frequency 9: #probs = Hash.new(0) 10: #each do |e| 11: # probs[e] += 1 12: #end 13: #probs 14: inject(Hash.new(0)){|h,v| h[v]+=1; h} 15: end
Like #/#, 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
# File lib/core/facets/enumerable/graph.rb, line 12 12: def graph(&yld) 13: if yld 14: h = {} 15: each do |*kv| 16: r = yld[*kv] 17: case r 18: when Hash 19: nk, nv = *r.to_a[0] 20: when Range 21: nk, nv = r.first, r.last 22: else 23: nk, nv = *r 24: end 25: h[nk] = nv 26: end 27: h 28: else 29: Enumerator.new(self,:graph) 30: end 31: end
# 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] } ["I had", 1, "dollar and", 50, "cents"].group_by { |e| e.class } #=> { String => ["I had","dollar and","cents"], Fixnum => [1,50] }
CREDIT: Erik Veenstra
# File lib/core/facets/enumerable/group_by.rb, line 17 17: def group_by #:yield: 18: #h = k = e = nil 19: r = Hash.new 20: each{ |e| (r[yield(e)] ||= []) << e } 21: r 22: end
Returns the maximum possible Shannon entropy of the array with given size assuming that it is an “order-0” source (each element is selected independently of the next).
CREDIT: Derek
# File lib/core/facets/enumerable/entropy.rb, line 30 30: def ideal_entropy 31: arr = to_a 32: unitProb = 1.0.to_f / arr.size.to_f 33: (1.to_f * arr.size.to_f * unitProb * Math.log(unitProb)/Math.log(2.to_f)) 34: end
DEPRECATE: This has been renamed to #.
# File lib/core/facets/enumerable/find_yield.rb, line 32 32: def map_detect(fallback=nil) 33: find_yield(fallback) 34: end
Send a message to each element and collect the result.
CREDIT: Sean O’Halpin
# File lib/core/facets/enumerable/map_send.rb, line 7 7: def map_send(meth, *args, &block) 8: map{|e| e.send(meth, *args, &block)} 9: end
Same as # but with an iteration counter.
a = [1,2,3].collect_with_index { |e,i| e*i } a #=> [0,2,6]
CREDIT: Gavin Sinclair
# File lib/core/facets/enumerable/map_with_index.rb, line 10 10: def map_with_index 11: r = [] 12: each_with_index do |e, i| 13: r << yield(e, i) 14: end 15: r 16: end
In Statistics mode is the value that occurs most frequently in a given set of data.
CREDIT: Robert Klemme
# File lib/core/facets/enumerable/mode.rb, line 8 8: def mode 9: max = 0 10: c = Hash.new 0 11: each {|x| cc = c[x] += 1; max = cc if cc > max} 12: c.select {|k,v| v == max}.map {|k,v| k} 13: end
Modulate. Divide an array into groups by modulo of the index.
CREDIT: Trans
NOTE: Would the better name for this be ‘collate’?
# File lib/core/facets/enumerable/modulate.rb, line 11 11: def modulate(modulo) 12: return to_a if modulo == 1 13: raise ArgumentError, 'bad modulo' if size % modulo != 0 14: r = Array.new(modulo, []) 15: (0...size).each do |i| 16: r[i % modulo] += [self[i]] 17: end 18: r 19: end
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
# File lib/core/facets/enumerable/none.rb, line 21 21: def none? # :yield: e 22: if block_given? 23: not self.any? { |e| yield e } 24: else 25: not self.any? 26: end 27: end
Returns a list on non-unique,
[1,1,2,2,3,4,5].nonuniq #=> [1,2]
CREDIT: Martin DeMello
# File lib/core/facets/enumerable/duplicates.rb, line 9 9: def nonuniq 10: h1 = {} 11: h2 = {} 12: each {|i| 13: h2[i] = true if h1[i] 14: h1[i] = true 15: } 16: h2.keys 17: end
Returns an array of elements for the elements that occur n times. Or according to the results of a given block.
[1,1,2,3,3,4,5,5].occur(1) #=> [2,4] [1,1,2,3,3,4,5,5].occur(2) #=> [1,3,5] [1,1,2,3,3,4,5,5].occur(3) #=> [] [1,2,2,3,3,3].occur(1..1) #=> [1] [1,2,2,3,3,3].occur(2..3) #=> [2,3] [1,1,2,3,3,4,5,5].occur { |n| n == 1 } #=> [2,4] [1,1,2,3,3,4,5,5].occur { |n| n > 1 } #=> [1,3,5]
# File lib/core/facets/enumerable/occur.rb, line 16 16: def occur(n=nil) #:yield: 17: result = Hash.new { |hash, key| hash[key] = Array.new } 18: self.each do |item| 19: key = item 20: result[key] << item 21: end 22: if block_given? 23: result.reject! { |key, values| ! yield(values.size) } 24: else 25: raise ArgumentError unless n 26: if Range === n 27: result.reject! { |key, values| ! n.include?(values.size) } 28: else 29: result.reject! { |key, values| values.size != n } 30: end 31: end 32: return result.values.flatten.uniq 33: end
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
# File lib/core/facets/enumerable/one.rb, line 21 21: def one? # :yield: e 22: matches = 0 23: if block_given? 24: self.each do |e| 25: if yield(e) 26: matches += 1 27: return false if matches > 1 28: end 29: end 30: return (matches == 1) 31: else 32: one? { |e| e } 33: end 34: end
Per element meta-functor.
[1,2,3].per + 3 #=> [4,5,6] [1,2,3].per(:map) + 3 #=> [4,5,6] [1,2,3].per(:select) > 1 #=> [2,3] [1,2,3].per.map + 3 #=> [4,5,6] [1,2,3].per.select > 1 #=> [2,3]
# File lib/core/facets/enumerable/per.rb, line 18 18: def per(enum_method=nil, *enum_args) 19: if enum_method 20: Permeator.new(self, enum_method, *enum_args) 21: #Functor.new do |op, *args| 22: # __send__(enum_method, *enum_args){ |x| x.__send__(op, *args) } #, &blk) } 23: #end 24: else 25: Functor.new do |enum_method, *enum_args| 26: Permeator.new(self, enum_method, *enum_args) 27: #Functor.new do |op, *args| 28: # __send__(enum_method, *enum_args){ |x| x.__send__(op, *args) } #, &blk) } 29: #end 30: end 31: end 32: end
Generates a hash mapping each unique element in the array to the relative frequency, i.e. the probablity, of it appearence.
CREDIT: Brian Schröder
# File lib/core/facets/enumerable/probability.rb, line 9 9: def probability 10: probs = Hash.new(0.0) 11: size = 0.0 12: each do | e | 13: probs[e] += 1.0 14: size += 1.0 15: end 16: probs.keys.each{ |e| probs[e] /= size } 17: probs 18: end
A versitle compaction method. Like # 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 # method, but to avoid potential issues associated with overriding core methods we use the alternate name #.
CREDIT: Trans
# File lib/core/facets/enumerable/purge.rb, line 27 27: def purge(*trash, &block) 28: trash = [nil] if trash.empty? 29: r = [] 30: if block_given? 31: each do |e| 32: y = yield(e) 33: r << e unless trash.any?{|t| t == y} 34: end 35: else 36: each do |e| 37: r << e unless trash.any?{|t| t == e} 38: end 39: end 40: r 41: end
# File lib/core/facets/enumerable/recursive.rb, line 3 3: def recursive(opts={}) 4: Recursor.new(self, opts) 5: end
Split on matching pattern. Unlike # this does not include matching elements.
['a1','a2','b1','a3','b2','a4'].split(/^b/) => [['a1','a2'],['a3'],['a4']]
CREDIT: Trans
# File lib/core/facets/enumerable/split.rb, line 10 10: def split(pattern) 11: memo = [] 12: sect = [] 13: each do |obj| 14: if pattern === obj 15: memo << sect 16: sect = [] 17: else 18: sect << obj 19: end 20: end 21: memo << sect 22: memo.pop while memo.last == [] 23: memo 24: end
Uses #+ to sum the enumerated elements.
[1,2,3].sum #=> 6 [3,3,3].sum #=> 9
# File lib/core/facets/enumerable/sum.rb, line 8 8: def sum(identity = 0, &block) 9: if block_given? 10: map(&block).sum 11: else 12: inject{ |sum, element| sum + element } || identity 13: end 14: end
Return the first n items from the collection
# File lib/core/facets/enumerable/take.rb, line 7 7: def take(n) 8: res = [] 9: count = 0 10: each do |e| 11: break if count >= n 12: res << e 13: count += 1 14: end 15: res 16: end
Like Enumerable#map but each iteration is processed via a separate thread.
CREDIT Sean O’Halpin
# File lib/more/facets/thread.rb, line 37 37: def threaded_map #:yield: 38: map{ |e| Thread.new(e){ |t| yield(t) } }.map{ |t| t.value } 39: end
Like Enumerable#map_send but each iteration is processed via a separate thread.
CREDIT Sean O’Halpin
# File lib/more/facets/thread.rb, line 46 46: def threaded_map_send(meth, *args, &block) 47: map{ |e| Thread.new(e){ |t| t.send(meth, *args, &block) } }.map{ |t| t.value } 48: end
Convert an Enumerable object into a hash by first turning it into an array.
CREDIT: Trans
# File lib/core/facets/to_hash.rb, line 225 225: def to_h(mode=nil) 226: to_a.to_h(mode) 227: end
# File lib/core/facets/to_hash.rb, line 241 241: def to_h_assoc 242: to_a.to_h_assoc 243: end
# File lib/core/facets/to_hash.rb, line 229 229: def to_h_auto 230: to_a.to_h_auto 231: end
# File lib/core/facets/to_hash.rb, line 237 237: def to_h_flat 238: to_a.to_h_flat 239: end
# File lib/core/facets/to_hash.rb, line 245 245: def to_h_multi 246: to_a.to_h_multi 247: end
# File lib/core/facets/to_hash.rb, line 233 233: def to_h_splat 234: to_a.to_h_splat 235: end
Like #, but determines uniqueness based on a given block.
(-5..5).to_a.uniq_by {|i| i*i }
produces
[-5, -4, -3, -2, -1, 0]
# File lib/core/facets/enumerable/uniq_by.rb, line 11 11: def uniq_by #:yield: 12: h = {}; inject([]) {|a,x| h[yield(x)] ||= a << x} 13: end
Recursively iterate over all Enumerable elements, or subset given :type=>[type1, type2, ...].
[1, 2, 8..9].visit{ |x| x.succ } # => [2, 3, [9, 10]]
# File lib/core/facets/enumerable/visit.rb, line 9 9: def visit(opts={}, &block) 10: type = opts[:type] ? [opts[:type]].flatten : [Enumerable] 11: skip = opts[:skip] 12: 13: map do |v| 14: case v 15: when String # b/c of 1.8 16: b.call(v) 17: when *type 18: v.recursive(opts).send(op,&b) 19: else 20: if skip && Enumerable === v 21: v 22: else 23: b.call(v) 24: end 25: end 26: end 27: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.