Module: Enumerable
- Defined in:
- lib/sixarm_ruby_ramp/enumerable.rb,
lib/sixarm_ruby_ramp/enumerable/map.rb,
lib/sixarm_ruby_ramp/enumerable/each.rb,
lib/sixarm_ruby_ramp/enumerable/nitems.rb,
lib/sixarm_ruby_ramp/enumerable/select.rb
Class Method Summary collapse
-
.cartesian_product(*enums) ⇒ Array
This is the fastest implementation we have found.
Instance Method Summary collapse
-
#bisect ⇒ Array<Array<Object>] an array of two arrays: the first array is the elements for which block is true, the second array is the elements for which block is false or nil.
Array<Array<Object>] an array of two arrays: the first array is the elements for which block is true, the second array is the elements for which block is false or nil.
-
#cartesian_product(*enums) ⇒ Object
Calculate the cartesian product.
-
#each_at(filter) ⇒ Object
Get each element at a given index or indices.
-
#each_at_normalize_filter(filter) ⇒ Object
Normalize the filter to make it respond to #each and #include?.
-
#each_at_strategy_with_optimization_max(filter) ⇒ Object
Implement #each_at by using a strategy with full optimization.
-
#each_at_strategy_with_optimization_min(filter) ⇒ Object
Implement #each_at by using a strategy with some optimization.
-
#each_at_strategy_with_optimization_off(filter) ⇒ Object
Implement #each_at by using a strategy with no optimization.
-
#hash_by ⇒ Hash<Object,Object>
Convert the enumerable to a hash by mapping each item to a pair [item, new item].
-
#index_by ⇒ Hash<Integer,Object>
Convert the enumerable to a hash by mapping each item to a pair [index ,item].
-
#intersect?(enum) ⇒ Boolean
A developer may want to optimize this implementation for other classes, such as detecting whether a range intersects another range simply by comparing the ranges’ min/max values.
-
#join(*op) ⇒ String
Shortcut to Array#join to concatenate the items into a string.
-
#map_id ⇒ Enumerable<Object>
Map each item => item.id.
-
#map_to_a ⇒ Enumberable<Array<Object>>
Map each item => item.to_a.
-
#map_to_f ⇒ Enumerable<Float>
Map each item => item.to_f.
-
#map_to_i ⇒ Enumerable<Integer>
Map each item => item.to_i.
-
#map_to_s ⇒ Enumerable<String>
Map each item => item.to_s.
-
#map_to_sym ⇒ Enumerable<Symbol>
Map each item => item.to_sym.
-
#map_with_index ⇒ Enumerable<Object>
Map each item and its index => a new output.
-
#mutex? ⇒ Boolean
Boolean true iff block is not false or nil, zero or one time.
-
#nitems?(n) ⇒ Boolean
True iff the block is not false or nil num times.
-
#nitems_until ⇒ Integer
The number of leading elements for which block is false.
-
#nitems_while ⇒ Integer
The number of leading elements for which block is not false or nil.
-
#nitems_with_index ⇒ Integer
Calls block with two arguments, the item and its index, for each item in enum.
-
#power_set ⇒ Array<Array<Object>>
Calculate the power set.
-
#select_until ⇒ Array<Object>
The leading elements for which block is falsey.
-
#select_while ⇒ Array<Object>
The leading elements for which block is truthy.
-
#select_with_index ⇒ Array<Object> the leading elements for which block is truthy.
Calls block with two arguments, the item and its index, for each item in enum.
- #to_h ⇒ Object
-
#to_h_merge ⇒ Hash<Object,Object>
Convert an enumerable to a hash and merge values.
Class Method Details
.cartesian_product(*enums) ⇒ Array
This is the fastest implementation we have found. It returns results in typical order.
For our benchmarks, we also compared these:
-
By William James, www.ruby-forum.com/topic/95519
-
By Brian Schröäer, blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/151857
199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 199 def self.cartesian_product(*enums) result = [[]] while [] != enums t, result = result, [] b, *enums = enums t.each do |a| b.each do |n| result << a + [n] end end end result end |
Instance Method Details
#bisect ⇒ Array<Array<Object>] an array of two arrays: the first array is the elements for which block is true, the second array is the elements for which block is false or nil.
Returns Array<Array<Object>] an array of two arrays: the first array is the elements for which block is true, the second array is the elements for which block is false or nil.
113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 113 def bisect accept=[] reject=[] each{|item| if yield(item) accept << item else reject << item end } return accept,reject end |
#cartesian_product(*enums) ⇒ Object
Calculate the cartesian product.
215 216 217 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 215 def cartesian_product(*enums) Enumerable.cartesian_product(self,*enums) end |
#each_at(filter) ⇒ Object
Get each element at a given index or indices.
Example: use an index.
["a", "b", "c", "d", "e"].each_at(1)
#=> "b"
Example: use an index that is negative when size is known.
["a", "b", "c", "d", "e"].each_at(-1)
=> "e"
Example: use a range.
["a", "b", "c", "d", "e"].each_at(1..3)
=> "b", "c", "d"
Example: use a range that has negatives when size is known.
["a", "b", "c", "d", "e"].each_at(-3..-1)
=> "c", "d", "e"
Example: use any object that responds to #each or #include?.
["a", "b", "c", "d", "e"].each_at([4, 2, -2])
=> "e", "c", "d"
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/sixarm_ruby_ramp/enumerable/each.rb', line 31 def each_at(filter) filter, optimize_for_whole_numbers = each_at_normalize_filter(filter) # Handle variations. # # Can we call self#at? # # * Yes: look up a self element by index. # * No: iterate on self, comparing the loop count to filter target. # # Can we call filter#each? # # * Yes: iterate on the filter elements. # * No: iterate on self, comparing the loop count to filter#include? # # Can we optimize for whole numbers? # # * Yes: we know that all filter targets are whole numbers. # * No: we must convert the filter target to an index size each time. # if self.respond_to?(:at) && filter.respond_to?(:each) if optimize_for_whole_numbers # each_at_strategy_with_self_at_and_filter_each_and_whole_numbers(filter) filter.each{|i| yield(at(i)) } else # each_at_strategy_with_self_at_and_filter_each(filter) filter.each{|i| yield(at(i >= 0 ? i : i + self.size)) } end elsif filter.respond_to?(:include?) # each_at_strategy_with_count(filter) i = 0 _size = respond_to?(:size) ? size : nil each{|e| yield(e) if (filter.include?(i) || (_size && filter.include(i - _size))) i += 1 } else raise ArgumentError end end |
#each_at_normalize_filter(filter) ⇒ Object
Normalize the filter to make it respond to #each and #include?.
If we can guarantee the filter is all whole numbers, then subsequent method calls can optimize the index lookup, by looking for the whole numbers instead of negative numbers.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/sixarm_ruby_ramp/enumerable/each.rb', line 132 def each_at_normalize_filter(filter) case filter when Numeric return [filter.to_i], filter >= 0 when Range if filter.first < 0 || filter.last < 0 min = filter.first.to_i; min += size if min < 0 max = filter.last.to_i; max += size if max < 0 max -= 1 if filter.exclude_end? filter = min..max end return filter, true else return filter, false end end |
#each_at_strategy_with_optimization_max(filter) ⇒ Object
Implement #each_at by using a strategy with full optimization.
When #each_at tests that both self#at is available and filter#each is available, and can test that all indexes are whole numbers, then #each_at calls this strategy.
120 121 122 123 124 |
# File 'lib/sixarm_ruby_ramp/enumerable/each.rb', line 120 def each_at_strategy_with_optimization_max(filter) filter.each{|i| yield(at(i)) } end |
#each_at_strategy_with_optimization_min(filter) ⇒ Object
Implement #each_at by using a strategy with some optimization.
When #each_at tests that both self#at is available and filter#each is available, yet cannot test that all indexes are whole numbers, then #each_at calls this strategy.
108 109 110 111 112 |
# File 'lib/sixarm_ruby_ramp/enumerable/each.rb', line 108 def each_at_strategy_with_optimization_min(filter) filter.each{|i| yield(at(i >= 0 ? i : i + self.size)) } end |
#each_at_strategy_with_optimization_off(filter) ⇒ Object
Implement #each_at by using a strategy with no optimization.
When #each_at tests that either self#at is unavailable or filter#each is unavailable, then #each_at calls this strategy.
This strategy uses a loop counter and iteration on the self elements, and each iteration, test whether the counter is in the filter.
This strategy is the slowest, and for the worst-case need. This strategy is rarely needed in the wild.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/sixarm_ruby_ramp/enumerable/each.rb', line 87 def each_at_strategy_with_optimization_off(filter) i = 0 if respond_to?(:size) each{|e| yield(e) if (filter.include?(i) || (size && filter.include?(i - size))) i += 1 } else each{|e| yield(e) if (filter.include?(i)) i += 1 } end end |
#hash_by ⇒ Hash<Object,Object>
Convert the enumerable to a hash by mapping each item to a pair [item, new item]
95 96 97 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 95 def hash_by map{|item| yield(item)}.to_h end |
#index_by ⇒ Hash<Integer,Object>
Convert the enumerable to a hash by mapping each item to a pair [index ,item]
Rails has this method.
From stackoverflow.com/questions/412771/cleanest-way-to-create-a-hash-from-an-array
80 81 82 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 80 def index_by inject({}) {|hash, elem| hash.merge!(yield(elem) => elem) } end |
#intersect?(enum) ⇒ Boolean
A developer may want to optimize this implementation for other classes, such as detecting whether a range intersects another range simply by comparing the ranges’ min/max values.
181 182 183 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 181 def intersect?(enum) return enum.any?{|item| self.include?(item)} end |
#join(*op) ⇒ String
Shortcut to Array#join to concatenate the items into a string
161 162 163 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 161 def join(*op) to_a.join(*op) end |
#map_id ⇒ Enumerable<Object>
Map each item => item.id
A typical use is to convert a list of ActiveRecord items to a list of id items.
This method is a faster way to get the same results as items.map(&:id)
16 17 18 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 16 def map_id map{|item| item.id} end |
#map_to_a ⇒ Enumberable<Array<Object>>
Map each item => item.to_a
This method is a faster way to get the same results as items.map(&:to_a)
30 31 32 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 30 def map_to_a map{|item| item.to_a} end |
#map_to_f ⇒ Enumerable<Float>
Map each item => item.to_f
A typical use is to convert a list of String items to a list of float items.
This method is a fast way to get the same results as items.map(&:to_f)
46 47 48 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 46 def map_to_f map{|item| item.to_f} end |
#map_to_i ⇒ Enumerable<Integer>
Map each item => item.to_i
A typical use is to convert a list of String items to a list of integer items.
This method is a fast way to get the same results as items.map(&:to_i)
62 63 64 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 62 def map_to_i map{|item| item.to_i} end |
#map_to_s ⇒ Enumerable<String>
Map each item => item.to_s
A typical use is to convert a list of Numeric items to a list of String items.
This method is a fast way to get the same results as items.map(&:to_s)
78 79 80 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 78 def map_to_s map{|item| item.to_s} end |
#map_to_sym ⇒ Enumerable<Symbol>
Map each item => item.to_sym
A typical use is to convert a list of Object items to a list of Symbol items.
This method is a fast way to get the same results as items.map(&:to_sym)
94 95 96 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 94 def map_to_sym map{|item| item.to_sym} end |
#map_with_index ⇒ Enumerable<Object>
Map each item and its index => a new output
109 110 111 112 |
# File 'lib/sixarm_ruby_ramp/enumerable/map.rb', line 109 def map_with_index index=-1 map{|item| index+=1; yield(item,index)} end |
#mutex? ⇒ Boolean
Returns boolean true iff block is not false or nil, zero or one time.
138 139 140 141 142 143 144 145 146 147 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 138 def mutex? num = 0 each{|item| if yield(item) num += 1 if num > 1 then return false end end } return true end |
#nitems?(n) ⇒ Boolean
Returns true iff the block is not false or nil num times.
10 11 12 13 14 15 16 17 18 19 |
# File 'lib/sixarm_ruby_ramp/enumerable/nitems.rb', line 10 def nitems?(n) num = 0 each{|item| if yield(item) num+=1 if num > n then return false end end } return num==n end |
#nitems_until ⇒ Integer
Returns the number of leading elements for which block is false.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/sixarm_ruby_ramp/enumerable/nitems.rb', line 39 def nitems_until num = 0 each{|item| if yield(item) break else num+=1 end } return num end |
#nitems_while ⇒ Integer
Returns the number of leading elements for which block is not false or nil.
27 28 29 30 31 |
# File 'lib/sixarm_ruby_ramp/enumerable/nitems.rb', line 27 def nitems_while num = 0 each{|item| yield(item) ? (num+=1) : break} return num end |
#nitems_with_index ⇒ Integer
Calls block with two arguments, the item and its index, for each item in enum.
60 61 62 63 64 |
# File 'lib/sixarm_ruby_ramp/enumerable/nitems.rb', line 60 def nitems_with_index index = 0 each{|item| yield(item,index) ? (index+=1) : break} return index end |
#power_set ⇒ Array<Array<Object>>
Calculate the power set.
This implementation is from the blog post below:
231 232 233 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 231 def power_set inject([[]]){|c,y|r=[];c.each{|i|r<<i;r<<i+[y]};r} end |
#select_until ⇒ Array<Object>
Returns the leading elements for which block is falsey.
22 23 24 25 26 |
# File 'lib/sixarm_ruby_ramp/enumerable/select.rb', line 22 def select_until arr = [] each{|item| yield(item) ? break : (arr << item)} return arr end |
#select_while ⇒ Array<Object>
Returns the leading elements for which block is truthy.
10 11 12 13 14 |
# File 'lib/sixarm_ruby_ramp/enumerable/select.rb', line 10 def select_while arr = [] each{|item| yield(item) ? (arr << item) : break} return arr end |
#select_with_index ⇒ Array<Object> the leading elements for which block is truthy.
Calls block with two arguments, the item and its index, for each item in enum.
36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/sixarm_ruby_ramp/enumerable/select.rb', line 36 def select_with_index index = 0 arr = [] each{|item| if yield(item,index) arr << item index+=1 else break end } return arr end |
#to_h ⇒ Object
22 23 24 25 26 27 28 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 22 def to_h hash={} each{|key,val| hash[key]=val } return hash end |
#to_h_merge ⇒ Hash<Object,Object>
Convert an enumerable to a hash and merge values.
If a key occurs more than once, then this will automatically merge the values to an array of the keys’ values.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/sixarm_ruby_ramp/enumerable.rb', line 46 def to_h_merge hash={} seen=Set.new each{|key,val| if hash.key? key if seen.include? key hash[key] << val else hash[key]=[hash[key]] hash[key] << val seen << key end else hash[key]=val end } return hash end |