epitools 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,306 @@
1
+
2
+
3
+ module Enumerable
4
+
5
+ #
6
+ # 'true' if the Enumerable has no elements
7
+ #
8
+ def blank?
9
+ not any?
10
+ end
11
+
12
+ #
13
+ # `.all` is more fun to type than `.to_a`
14
+ #
15
+ alias_method :all, :to_a
16
+
17
+ #
18
+ # `includes?` is gramatically correct.
19
+ #
20
+ alias_method :includes?, :include?
21
+
22
+
23
+ #
24
+ # Skip the first n elements and return an Enumerator for the rest, or pass them
25
+ # in succession to the block, if given. This is like "drop", but returns an enumerator
26
+ # instead of converting the whole thing to an array.
27
+ #
28
+ def skip(n)
29
+ if block_given?
30
+ each do |x|
31
+ if n > 0
32
+ n -= 1
33
+ else
34
+ yield x
35
+ end
36
+ end
37
+ else
38
+ enum_for :skip, n
39
+ end
40
+ end
41
+
42
+ #
43
+ # Split this enumerable into chunks, given some boundary condition. (Returns an array of arrays.)
44
+ #
45
+ # Options:
46
+ # :include_boundary => true #=> include the element that you're splitting at in the results
47
+ # (default: false)
48
+ # :after => true #=> split after the matched element (only has an effect when used with :include_boundary)
49
+ # (default: false)
50
+ # :once => flase #=> only perform one split (default: false)
51
+ #
52
+ # Examples:
53
+ # [1,2,3,4,5].split{ |e| e == 3 }
54
+ # #=> [ [1,2], [4,5] ]
55
+ #
56
+ # [1,2,3,4,5].split(:include_boundary=>true) { |e| e == 3 }
57
+ # #=> [ [1,2], [3,4,5] ]
58
+ #
59
+ # chapters = File.read("ebook.txt").split(/Chapter \d+/, :include_boundary=>true)
60
+ # #=> [ ["Chapter 1", ...], ["Chapter 2", ...], etc. ]
61
+ #
62
+ def split_at(matcher=nil, options={}, &block)
63
+ # TODO: Ruby 1.9 returns Enumerators for everything now. Maybe use that?
64
+
65
+ return self unless self.any?
66
+
67
+ include_boundary = options[:include_boundary] || false
68
+
69
+ if matcher.nil?
70
+ boundary_test_proc = block
71
+ else
72
+ if matcher.is_a? String or matcher.is_a? Regexp
73
+ boundary_test_proc = proc { |element| element[matcher] rescue nil }
74
+ else
75
+ boundary_test_proc = proc { |element| element == matcher }
76
+ #raise "I don't know how to split with #{matcher}"
77
+ end
78
+ end
79
+
80
+ chunks = []
81
+ current_chunk = []
82
+
83
+ splits = 0
84
+ max_splits = options[:once] == true ? 1 : options[:max_splits]
85
+
86
+ each do |e|
87
+
88
+ if boundary_test_proc.call(e) and (max_splits == nil or splits < max_splits)
89
+
90
+ if current_chunk.empty? and not include_boundary
91
+ next # hit 2 boundaries in a row... just keep moving, people!
92
+ end
93
+
94
+ if options[:after]
95
+ # split after boundary
96
+ current_chunk << e if include_boundary # include the boundary, if necessary
97
+ chunks << current_chunk # shift everything after the boundary into the resultset
98
+ current_chunk = [] # start a new result
99
+ else
100
+ # split before boundary
101
+ chunks << current_chunk # shift before the boundary into the resultset
102
+ current_chunk = [] # start a new result
103
+ current_chunk << e if include_boundary # include the boundary, if necessary
104
+ end
105
+
106
+ splits += 1
107
+
108
+ else
109
+ current_chunk << e
110
+ end
111
+
112
+ end
113
+
114
+ chunks << current_chunk if current_chunk.any?
115
+
116
+ chunks # resultset
117
+ end
118
+
119
+ #
120
+ # Split the array into chunks, cutting between the matched element and the next element.
121
+ #
122
+ # Example:
123
+ # [1,2,3,4].split_after{|e| e == 3 } #=> [ [1,2,3], [4] ]
124
+ #
125
+ def split_after(matcher=nil, options={}, &block)
126
+ options[:after] ||= true
127
+ options[:include_boundary] ||= true
128
+ split_at(matcher, options, &block)
129
+ end
130
+
131
+ #
132
+ # Split the array into chunks, cutting between the matched element and the previous element.
133
+ #
134
+ # Example:
135
+ # [1,2,3,4].split_before{|e| e == 3 } #=> [ [1,2], [3,4] ]
136
+ #
137
+ def split_before(matcher=nil, options={}, &block)
138
+ options[:include_boundary] ||= true
139
+ split_at(matcher, options, &block)
140
+ end
141
+
142
+ #
143
+ # Sum the elements
144
+ #
145
+ def sum
146
+ if block_given?
147
+ inject(0) { |total,elem| total + yield(elem) }
148
+ else
149
+ inject(0) { |total,elem| total + elem }
150
+ end
151
+ end
152
+
153
+ #
154
+ # Average the elements
155
+ #
156
+ def average
157
+ count = 0
158
+ sum = inject(0) { |total,n| count += 1; total + n }
159
+ sum / count.to_f
160
+ end
161
+
162
+ #
163
+ # The same as "map", except that if an element is an Array or Enumerable, map is called
164
+ # recursively on that element.
165
+ #
166
+ # Example:
167
+ # [ [1,2], [3,4] ].deep_map{|e| e ** 2 } #=> [ [1,4], [9,16] ]
168
+ #
169
+ def deep_map(depth=nil, &block)
170
+ map do |obj|
171
+
172
+ case obj
173
+ when Enumerable
174
+ obj.deep_map(&block)
175
+ else
176
+ block.call(obj)
177
+ end
178
+
179
+ end
180
+ end
181
+
182
+ alias_method :recursive_map, :deep_map
183
+ alias_method :map_recursively, :deep_map
184
+ alias_method :map_recursive, :deep_map
185
+
186
+ #
187
+ # The same as "select", except that if an element is an Array or Enumerable, select is called
188
+ # recursively on that element.
189
+ #
190
+ # Example:
191
+ # [ [1,2], [3,4] ].deep_map{|e| e ** 2 } #=> [ [1,4], [9,16] ]
192
+ #
193
+ def deep_select(depth=nil, &block)
194
+ select do |e|
195
+ if (e.is_a? Array or e.is_a? Enumerable) and (depth && depth > 0)
196
+ e.select(depth-1, &block)
197
+ else
198
+ block.call(e)
199
+ end
200
+ end
201
+ end
202
+
203
+ alias_method :recursive_select, :deep_select
204
+
205
+
206
+ #
207
+ # Identical to "reduce" in ruby1.9 (or foldl in haskell.)
208
+ #
209
+ # Example:
210
+ # array.foldl{|a,b| a + b } == array[1..-1].inject(array[0]){|a,b| a + b }
211
+ #
212
+ def foldl(methodname=nil, &block)
213
+ result = nil
214
+
215
+ raise "Error: pass a parameter OR a block, not both!" unless !!methodname ^ block_given?
216
+
217
+ if methodname
218
+
219
+ each_with_index do |e,i|
220
+ if i == 0
221
+ result = e
222
+ next
223
+ end
224
+
225
+ result = result.send(methodname, e)
226
+ end
227
+
228
+ else
229
+
230
+ each_with_index do |e,i|
231
+ if i == 0
232
+ result = e
233
+ next
234
+ end
235
+
236
+ result = block.call(result, e)
237
+ end
238
+
239
+ end
240
+
241
+ result
242
+ end
243
+
244
+ #
245
+ # Returns the powerset of the Enumerable
246
+ #
247
+ # Example:
248
+ # [1,2].powerset #=> [[], [1], [2], [1, 2]]
249
+ #
250
+ def powerset
251
+ # the bit pattern of the numbers from 0..2^(elements)-1 can be used to select the elements of the set...
252
+ a = to_a
253
+ (0...2**a.size).map do |bitmask|
254
+ a.select.with_index{ |e, i| bitmask[i] == 1 }
255
+ end
256
+ end
257
+
258
+ #
259
+ # Does the opposite of #zip -- converts [ [:a, 1], [:b, 2] ] to [ [:a, :b], [1, 2] ]
260
+ #
261
+ def unzip
262
+ # TODO: make it work for arrays containing uneven-length contents
263
+ to_a.transpose
264
+ end
265
+
266
+ #
267
+ # Associative grouping; groups all elements who share something in common with each other.
268
+ # You supply a block which takes two elements, and have it return true if they are "neighbours"
269
+ # (eg: belong in the same group).
270
+ #
271
+ # Example:
272
+ # [1,2,5,6].group_neighbours_by { |a,b| b-a <= 1 } #=> [ [1,2], [5,6] ]
273
+ #
274
+ # (Note: This is a very fast one-pass algorithm -- therefore, the groups must be pre-sorted.)
275
+ #
276
+ def group_neighbours_by(&block)
277
+ result = []
278
+ cluster = [first]
279
+ each_cons(2) do |a,b|
280
+ if yield(a,b)
281
+ cluster << b
282
+ else
283
+ result << cluster
284
+ cluster = [b]
285
+ end
286
+ end
287
+
288
+ result << cluster if cluster.any?
289
+
290
+ result
291
+ end
292
+ alias_method :group_neighbors_by, :group_neighbours_by
293
+
294
+
295
+ #
296
+ # Convert the array into a stable iterator (Iter) object.
297
+ #
298
+ def to_iter
299
+ Iter.new(to_a)
300
+ end
301
+ alias_method :iter, :to_iter
302
+
303
+
304
+ end
305
+
306
+
@@ -0,0 +1,201 @@
1
+
2
+ class Hash
3
+
4
+ #
5
+ # 'true' if the Hash has no entries
6
+ #
7
+ def blank?
8
+ not any?
9
+ end
10
+
11
+ #
12
+ # Runs "remove_blank_values" on self.
13
+ #
14
+ def remove_blank_values!
15
+ delete_if{|k,v| v.blank?}
16
+ self
17
+ end
18
+
19
+ #
20
+ # Returns a new Hash where blank values have been removed.
21
+ # (It checks if the value is blank by calling #blank? on it)
22
+ #
23
+ def remove_blank_values
24
+ dup.remove_blank_values!
25
+ end
26
+
27
+ #
28
+ # Runs map_values on self.
29
+ #
30
+ def map_values!(&block)
31
+ keys.each do |key|
32
+ value = self[key]
33
+ self[key] = yield(value)
34
+ end
35
+ self
36
+ end
37
+
38
+ #
39
+ # Transforms the values of the hash by passing them into the supplied
40
+ # block, and then using the block's result as the new value.
41
+ #
42
+ def map_values(&block)
43
+ dup.map_values!(&block)
44
+ end
45
+
46
+ #
47
+ # Runs map_keys on self.
48
+ #
49
+ def map_keys!(&block)
50
+ keys.each do |key|
51
+ value = delete(key)
52
+ self[yield(key)] = value
53
+ end
54
+ self
55
+ end
56
+
57
+ #
58
+ # Transforms the keys of the hash by passing them into the supplied block,
59
+ # and then using the blocks result as the new key.
60
+ #
61
+ def map_keys(&block)
62
+ dup.map_keys!(&block)
63
+ end
64
+
65
+ #
66
+ # Returns a new Hash whose values default to empty arrays. (Good for collecting things!)
67
+ #
68
+ # eg:
69
+ # Hash.of_arrays[:yays] << "YAY!"
70
+ #
71
+ def self.of_arrays
72
+ new {|h,k| h[k] = [] }
73
+ end
74
+
75
+ #
76
+ # Returns a new Hash whose values default to 0. (Good for counting things!)
77
+ #
78
+ # eg:
79
+ # Hash.of_integers[:yays] += 1
80
+ #
81
+ def self.of_integers
82
+ new(0)
83
+ end
84
+
85
+ #
86
+ # Hash keys become methods, kinda like OpenStruct. These methods have the lowest priority,
87
+ # so be careful. They will be overridden by any methods on Hash.
88
+ #
89
+ def self.lazy!
90
+ Hash.class_eval do
91
+ def method_missing(name, *args)
92
+ if args.any?
93
+ super
94
+ else
95
+ self[name] || self[name.to_s]
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ #
102
+ # `key?` and `includes?` is an alias for `include?`
103
+ #
104
+ alias_method :key?, :include?
105
+ alias_method :includes?, :include?
106
+
107
+ #
108
+ # Makes each element in the `path` array point to a hash containing the next element in the `path`.
109
+ # Useful for turning a bunch of strings (paths, module names, etc.) into a tree.
110
+ #
111
+ # Example:
112
+ # h = {}
113
+ # h.mkdir_p(["a", "b", "c"]) #=> {"a"=>{"b"=>{"c"=>{}}}}
114
+ # h.mkdir_p(["a", "b", "whoa"]) #=> {"a"=>{"b"=>{"c"=>{}, "whoa"=>{}}}}
115
+ #
116
+ def mkdir_p(path)
117
+ return if path.empty?
118
+ dir = path.first
119
+ self[dir] ||= {}
120
+ self[dir].mkdir_p(path[1..-1])
121
+ self
122
+ end
123
+
124
+ #
125
+ # Turn some nested hashes into a tree (returns an array of strings, padded on the left with indents.)
126
+ #
127
+ def tree(level=0, indent=" ")
128
+ result = []
129
+ dent = indent * level
130
+ each do |key, val|
131
+ result << dent+key
132
+ result += val.tree(level+1) if val.any?
133
+ end
134
+ result
135
+ end
136
+
137
+ #
138
+ # Print the result of `tree`
139
+ #
140
+ def print_tree
141
+ tree.each { |row| puts row }
142
+ nil
143
+ end
144
+
145
+ #
146
+ # Convert the hash into a GET query.
147
+ #
148
+ def to_query
149
+ params = ''
150
+ stack = []
151
+
152
+ each do |k, v|
153
+ if v.is_a?(Hash)
154
+ stack << [k,v]
155
+ else
156
+ params << "#{k}=#{v}&"
157
+ end
158
+ end
159
+
160
+ stack.each do |parent, hash|
161
+ hash.each do |k, v|
162
+ if v.is_a?(Hash)
163
+ stack << ["#{parent}[#{k}]", v]
164
+ else
165
+ params << "#{parent}[#{k}]=#{v}&"
166
+ end
167
+ end
168
+ end
169
+
170
+ params.chop! # trailing &
171
+ params
172
+ end
173
+
174
+ #
175
+ # Query a hash using MQL (see: http://wiki.freebase.com/wiki/MQL_operators for reference)
176
+ #
177
+ # Examples:
178
+ # > query(name: /steve/)
179
+ # > query(/title/ => ??)
180
+ # > query(articles: [{title: ??}])
181
+ # > query(responses: [])
182
+ # > query("date_of_birth<" => "2000")
183
+ #
184
+ def query(template)
185
+ results = []
186
+ template.each do |key,val|
187
+ case key
188
+ when Regexp, String
189
+ when Array
190
+ when Hash
191
+ results += hash.query(template)
192
+ end
193
+ end
194
+
195
+ map do |key,val|
196
+ end
197
+ end
198
+ alias_method :mql, :query
199
+
200
+ end
201
+