Module: Enumerable
- Defined in:
- lib/nano/enumerable/one%3F.rb,
lib/nano/enumerable/eF.rb,
lib/nano/enumerable/%25.rb,
lib/nano/enumerable/to_h.rb,
lib/nano/enumerable/cross.rb,
lib/nano/enumerable/graph.rb,
lib/nano/enumerable/occur.rb,
lib/nano/enumerable/%2A%2A.rb,
lib/nano/enumerable/each_by.rb,
lib/nano/enumerable/entropy.rb,
lib/nano/enumerable/none%3F.rb,
lib/nano/enumerable/frequency.rb,
lib/nano/enumerable/unique_by.rb,
lib/nano/enumerable/each_slice.rb,
lib/nano/enumerable/filter_map.rb,
lib/nano/enumerable/%3A%3Across.rb,
lib/nano/enumerable/commonality.rb,
lib/nano/enumerable/compact_map.rb,
lib/nano/enumerable/probability.rb,
lib/nano/enumerable/partition_by.rb,
lib/nano/enumerable/ideal_entropy.rb,
lib/nano/enumerable/filter_collect.rb,
lib/nano/enumerable/map_with_index.rb,
lib/nano/enumerable/compact_collect.rb,
lib/nano/enumerable/find_collisions.rb,
lib/nano/enumerable/map_with_counter.rb,
lib/nano/enumerable/%3A%3Acombinations.rb,
lib/nano/enumerable/collect_with_index.rb,
lib/nano/enumerable/collect_with_counter.rb
Overview
– Credit goes to Florian Gross. ++
Class Method Summary collapse
-
.combinations(head, *rest) ⇒ Object
Produces an array of arrays of all possible combinations of the given arrays in the position given.
-
.cross(*enums, &block) ⇒ Object
Provides the cross-product of two or more Enumerables.
Instance Method Summary collapse
-
#**(enum) ⇒ Object
Operator alias for cross-product.
-
#collect_with_counter ⇒ Object
Same as #collect but with an iteration counter.
- #collect_with_index ⇒ Object
-
#commonality(&block) ⇒ Object
Returns all items that are equal in terms of the supplied block.
-
#compact_collect ⇒ Object
Collects/Maps and compacts items in one single step.
- #compact_map ⇒ Object
-
#cross(*enums, &block) ⇒ Object
The instance level cross-product method.
- #each_by ⇒ Object
-
#each_slice(step = nil, &yld) ⇒ Object
Iterate through slices.
-
#eF ⇒ Object
(also: #%)
Returns an elementwise Functor.
-
#entropy ⇒ Object
Shannon’s entropy for an array - returns the average bits per symbol required to encode the array.
-
#filter_collect ⇒ Object
Collects/Maps and filters items out in one single step.
- #filter_map ⇒ Object
-
#find_collisions(&blk) ⇒ Object
Like commonality but returns an array if no block is given.
-
#frequency ⇒ Object
Generates a hash mapping each unique symbol in the array to the absolute frequency it appears.
-
#graph(&yld) ⇒ Object
Like
#map
/#collect
, but it generates a Hash. -
#ideal_entropy ⇒ Object
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).
- #map_with_counter ⇒ Object
- #map_with_index ⇒ Object
-
#none? ⇒ Boolean
Enumerable#none? is the logical opposite of the builtin method Enumerable#any?.
-
#occur(n = nil) ⇒ Object
Returns an array of elements for the elements that occur n times.
-
#one? ⇒ Boolean
Enumerable#one? returns
true
if and only if exactly one element in the collection satisfies the given predicate. -
#partition_by ⇒ Object
See Enumerable#partition for the background.
-
#probability ⇒ Object
Generates a hash mapping each unique symbol in the array to the relative frequency, i.e.
-
#to_h ⇒ Object
Produces a hash from an Enumerable with index for keys.
-
#uniq_by ⇒ Object
Like #uniq, but determines uniqueness based on a given block.
Class Method Details
.combinations(head, *rest) ⇒ Object
Produces an array of arrays of all possible combinations of the given arrays in the position given. (Explain me better?)
a = %w|a b|
b = %w|a x|
c = %w|x y|
Array.combinations(a, b, c).each { |x| p x }
produces
["a", "a", "x"]
["a", "a", "y"]
["a", "x", "x"]
["a", "x", "y"]
["b", "a", "x"]
["b", "a", "y"]
["b", "x", "x"]
["b", "x", "y"]
23 24 25 26 27 28 |
# File 'lib/nano/enumerable/%3A%3Acombinations.rb', line 23 def self.combinations(head, *rest) crest = rest.empty? ? [[]] : combinations(*rest) head.inject([]) { |combs, item| combs + crest.map { |comb| [item] + comb } } end |
.cross(*enums, &block) ⇒ Object
Provides the cross-product of two or more Enumerables.
This is the class-level method. The instance method
calls on this.
Enumerable.cross([1,2], [4], ["apple", "banana"])
#=> [[1, 4, "apple"], [1, 4, "banana"], [2, 4, "apple"], [2, 4, "banana"]]
Enumerable.cross([1,2], [3,4])
#=> [[1, 3], [1, 4], [2, 3], [2, 4]]
18 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/nano/enumerable/%3A%3Across.rb', line 18 def self.cross(*enums, &block) raise if enums.empty? gens = enums.map{|e| Generator.new(e)} return [] if gens.any? {|g| g.end?} sz = gens.size res = [] tuple = Array.new(sz) loop do # fill tuple (0 ... sz).each { |i| tuple[i] = gens[i].current } if block.nil? res << tuple.dup else block.call(tuple.dup) end # step forward gens[-1].next (sz-1).downto(0) { |i| if gens[i].end? if i > 0 gens[i].rewind gens[i-1].next else return res end end } end end |
Instance Method Details
#**(enum) ⇒ Object
Operator alias for cross-product.
a = [1,2] ** [4,5]
a #=> [[1, 4],[1, 5],[2, 4],[2, 5]]
11 12 13 |
# File 'lib/nano/enumerable/%2A%2A.rb', line 11 def **(enum) Enumerable.cross(self, enum) end |
#collect_with_counter ⇒ Object
Same as #collect but with an iteration counter.
a = [1,2,3].collect_with_counter{ |e,i| e*i }
a #=> [0,2,6]
– Why the term counter? There may be a change in Ruby 2.0 to use this word instead of index. Index will still be used for Array, since that is the proper meaning in that context. In the mean time, aliases are provided. ++
13 14 15 16 17 18 19 |
# File 'lib/nano/enumerable/collect_with_counter.rb', line 13 def collect_with_counter r = [] each_index do |i| r << yield(self[i], i) end r end |
#collect_with_index ⇒ Object
3 |
# File 'lib/nano/enumerable/collect_with_index.rb', line 3 alias_method( :collect_with_index, :collect_with_counter ) |
#commonality(&block) ⇒ Object
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. No guarantee about the order of the elements in the resulting array is made.
Note: The result will be an array if you supply no block and a hash otherwise.
[1, 2, 2, 3, 4, 4].commonality # => { 2 => [2], 4 => [4] }
["foo", "bar", "a"].commonality { |str| str.length }
# => { 2 => ["foo, "bar"] }
# Returns all persons that share their last name with another person.
persons.collisions { |person| person.last_name }
– Credit goes to Florian Gross ++
22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/nano/enumerable/commonality.rb', line 22 def commonality( &block ) had_no_block = !block block ||= lambda { |item| item } result = Hash.new { |hash, key| hash[key] = Array.new } self.each do |item| key = block.call(item) result[key] << item end result.reject! do |key, values| values.size <= 1 end #return had_no_block ? result.values.flatten : result return result end |
#compact_collect ⇒ Object
Collects/Maps and compacts items in one single step. The items for which the supplied block returns nil
will not end up in the resulting array.
# Return telephone numbers of all persons that have a telephone number.
persons.compact_collect { |person| person.telephone_no }
Also see Enumerable#collect, Enumerable#map, Array#compact.
17 18 19 20 21 22 23 |
# File 'lib/nano/enumerable/compact_collect.rb', line 17 def compact_collect #:yield: filter_collect do |item| new_item = yield(item) throw(:skip) if new_item.nil? new_item end end |
#compact_map ⇒ Object
6 |
# File 'lib/nano/enumerable/compact_map.rb', line 6 alias_method :compact_map, :compact_collect |
#cross(*enums, &block) ⇒ Object
The instance level cross-product method.
a = []
[1,2].cross([4,5]){|elem| a << elem }
a #=> [[1, 4],[1, 5],[2, 4],[2, 5]]
14 15 16 |
# File 'lib/nano/enumerable/cross.rb', line 14 def cross(*enums, &block) Enumerable.cross(self, *enums, &block) end |
#each_by ⇒ Object
4 |
# File 'lib/nano/enumerable/each_by.rb', line 4 alias_method( :each_by, :each_slice ) |
#each_slice(step = nil, &yld) ⇒ Object
Iterate through slices. If slicing step is not given, the the arity if the block is used.
x = []
[1,2,3,4].each_slice(2){ |a,b| x << [a,b] }
x #=> [ [1,2], [3,4] ]
x = []
[1,2,3,4,5,6].each_slice(3){ |a| x << a }
x #=> [ [1,2,3], [4,5,6] ]
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/nano/enumerable/each_slice.rb', line 15 def each_slice(step=nil, &yld) if step cnt = step n = length / cnt n += 1 if length % cnt > 0 n.times { |i| yld.call(*self.slice(i*cnt,cnt)) } return cnt else cnt = yld.arity.abs n = length / cnt n += 1 if length % cnt > 0 n.times { |i| yld.call(*self.slice(i*cnt,cnt)) } return cnt end end |
#eF ⇒ Object Also known as: %
Returns an elementwise Functor. This makes R-like elementwise operations possible.
a = [1,2,3]
b = [4,5]
p a.eF + 3 => [4,5,6]
p a.eF + b => [5,7]
p a.eF.+(b,2) => [[5,7],[3,4,5]]
NOTE: This method is still undergoing some fine tuning. It is questionable as to whether different sized arrays should be padded with nil (or a parameter) and whether multiple parameters are a good idea (eg. a.%.+(b,2,...)
).
– Special thanks to Martin DeMello for helping to develop this. ++
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/nano/enumerable/eF.rb', line 34 def eF #Functor.new(self) do |op,*args| Functor.new do |op,*args| a = args.collect do |arg| if arg.kind_of?(Enumerable) ln = ( arg.length > self.length ? self.length : arg.length ) self[0...ln].zip(arg[0...ln]).collect{ |a,b| a.send(op,b) } #self[0...ln].zip(arg[0...1n]).collect{ |a,b| b ? a.send(op,b) : nil } else self.collect{ |a| a.send(op,arg) } end end a.flatten! if args.length == 1 a end end |
#entropy ⇒ Object
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 #=>
15 16 17 18 19 20 21 22 23 |
# File 'lib/nano/enumerable/entropy.rb', line 15 def entropy arr = self.to_a probHash = arr.probability # h is the Shannon entropy of the array h = -1.to_f * probHash.keys.inject(0.to_f) do |sum, i| sum + (probHash[i] * (Math.log(probHash[i])/Math.log(2.to_f))) end h end |
#filter_collect ⇒ Object
Collects/Maps and filters items out in one single step. You can use throw(:skip) in the supplied block to indicate that the current item should not end up in the resulting array.
# Return names of all person with an age of at least 18.
persons.filter_collect do |person|
throw(:skip) if person.age < 18
person.name
end
Also see Enumerable#collect, Enumerable#find_all.
18 19 20 21 22 23 24 25 26 27 |
# File 'lib/nano/enumerable/filter_collect.rb', line 18 def filter_collect #:yield: result = [] self.each do |item| catch(:skip) do new_item = yield(item) result << new_item end end return result end |
#filter_map ⇒ Object
6 |
# File 'lib/nano/enumerable/filter_map.rb', line 6 alias_method :filter_map, :filter_collect |
#find_collisions(&blk) ⇒ Object
Like commonality but returns an array if no block is given.
7 8 9 10 11 12 13 |
# File 'lib/nano/enumerable/find_collisions.rb', line 7 def find_collisions( &blk ) #:yield: if block_given? commonality( &blk ) else commonality.values.flatten.uniq end end |
#frequency ⇒ Object
Generates a hash mapping each unique symbol in the array to the absolute frequency it appears.
8 9 10 11 12 13 14 |
# File 'lib/nano/enumerable/frequency.rb', line 8 def frequency probs = Hash.new(0) each do | e | probs[e] += 1 end probs end |
#graph(&yld) ⇒ Object
Like #map
/#collect
, but it 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 }
– Credits for original work goes to Zallus Kanite and Gavin Sinclair. ++
13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/nano/enumerable/graph.rb', line 13 def graph(&yld) if yld inject({}) do |h,kv| nk, nv = yld[*kv] h[nk] = nv h end else Hash[*self.to_a.flatten] end end |
#ideal_entropy ⇒ Object
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 goes to Derek. ++
12 13 14 15 16 |
# File 'lib/nano/enumerable/ideal_entropy.rb', line 12 def ideal_entropy arr = self.to_a unitProb = 1.0.to_f / arr.size.to_f (-1.to_f * arr.size.to_f * unitProb * Math.log(unitProb)/Math.log(2.to_f)) end |
#map_with_counter ⇒ Object
3 |
# File 'lib/nano/enumerable/map_with_counter.rb', line 3 alias_method( :map_with_counter, :collect_with_counter ) |
#map_with_index ⇒ Object
3 |
# File 'lib/nano/enumerable/map_with_index.rb', line 3 alias_method( :map_with_index, :collect_with_counter ) |
#none? ⇒ Boolean
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
20 21 22 23 24 25 26 |
# File 'lib/nano/enumerable/none%3F.rb', line 20 def none? # :yield: e if block_given? not self.any? { |e| yield e } else not self.any? end 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.
[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]
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/nano/enumerable/occur.rb', line 17 def occur(n=nil) #:yield: result = Hash.new { |hash, key| hash[key] = Array.new } self.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 |
#one? ⇒ Boolean
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
20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/nano/enumerable/one%3F.rb', line 20 def one? # :yield: e matches = 0 if block_given? self.each do |e| if yield(e) matches += 1 return false if matches > 1 end end return (matches == 1) else one? { |e| e } end end |
#partition_by ⇒ Object
See Enumerable#partition for the background. #partition_by is best explained by example.
(1..5).partition_by { |n| n % 3 }
#=> { 0 => [3], 1 => [1, 4], 2 => [2,5] }
["I had", 1, "dollar and", 50, "cents"].partition_by { |e| e.class }
#=> { String => ["I had","dollar and","cents"], Fixnum => [1,50] }
#partition_by 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. – Credit goes to Gavin Sinclair. ++
19 20 21 22 23 24 25 26 |
# File 'lib/nano/enumerable/partition_by.rb', line 19 def partition_by #:yield: result = {} self.each do |e| value = yield e (result[value] ||= []) << e end result end |
#probability ⇒ Object
Generates a hash mapping each unique symbol in the array to the relative frequency, i.e. the probablity, of it appearence.
10 11 12 13 14 15 16 17 18 19 |
# File 'lib/nano/enumerable/probability.rb', line 10 def probability probs = Hash.new(0.0) size = 0.0 each do | e | probs[e] += 1.0 size += 1.0 end probs.keys.each do |e| probs[e] /= size end probs end |
#to_h ⇒ Object
Produces a hash from an Enumerable with index for keys.
a1 = [ :a, :b ]
a1.to_h #=> { 0=>:a, 1=>:b }
8 9 10 11 12 |
# File 'lib/nano/enumerable/to_h.rb', line 8 def to_h h = {} each_with_index{ |e,i| h[i] = e } h end |
#uniq_by ⇒ Object
Like #uniq, but determines uniqueness based on a given block.
(-5..5).to_a.uniq_by {|i| i*i }
produces
[-5, -4, -3, -2, -1, 0]
12 13 14 |
# File 'lib/nano/enumerable/unique_by.rb', line 12 def uniq_by #:yield: h = {}; inject([]) {|a,x| h[yield(x)] ||= a << x} end |