davidrichards-just_enumerable_stats 0.0.3 → 0.0.4

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.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 0
4
- :patch: 3
4
+ :patch: 4
@@ -2,6 +2,12 @@
2
2
  $:.unshift File.dirname(__FILE__)
3
3
  require 'fixed_range'
4
4
 
5
+ begin
6
+ require 'facets/dictionary'
7
+ rescue LoadError => e
8
+ # Do nothing
9
+ end
10
+
5
11
  # Borrowed this from my own gem, sirb
6
12
 
7
13
  class Object
@@ -215,7 +221,9 @@ module Enumerable
215
221
  # For non-numeric values, returns a unique set,
216
222
  # ordered if possible.
217
223
  def categories
218
- if self.is_numeric?
224
+ if @categories
225
+ @categories
226
+ elsif self.is_numeric?
219
227
  self.range_instance.map
220
228
  else
221
229
  self.uniq.sort rescue self.uniq
@@ -239,8 +247,61 @@ module Enumerable
239
247
  self.range_class
240
248
  end
241
249
 
250
+ # Takes a hash of arrays for categories
251
+ # If Facets happens to be loaded on the computer, this keeps the order
252
+ # of the categories straight.
253
+ def set_range(hash)
254
+ if defined?(Dictionary)
255
+ @range_hash = Dictionary.new
256
+ @range_hash.merge!(hash)
257
+ @categories = @range_hash.keys
258
+ else
259
+ @categories = hash.keys
260
+ @range_hash = hash
261
+ end
262
+ @categories
263
+ end
264
+
265
+ # The hash of lambdas that are used to categorize the enumerable.
266
+ attr_reader :range_hash
267
+
268
+ # The arguments needed to instantiate the custom-defined range class.
242
269
  attr_reader :range_class_args
243
270
 
271
+ # Counts each element where the block evaluates to true
272
+ # Example:
273
+ # a = [1,2,3]
274
+ # a.count_if {|e| e % 2 == 0}
275
+ def count_if(&block)
276
+ self.inject(0) do |s, e|
277
+ s += 1 if block.call(e)
278
+ s
279
+ end
280
+ end
281
+
282
+ # Returns a Hash or Dictionary (if available) for each category with a
283
+ # value as the set of matching values as an array.
284
+ # Because this is supposed to be lean (just enumerables), but this is an
285
+ # expensive call, I'm going to cache it and offer a parameter to reset
286
+ # the cache. So, call category_values(true) if you need to reset the
287
+ # cache.
288
+ def category_values(reset=false)
289
+ @category_values = nil if reset
290
+ return @category_values if @category_values
291
+ container = defined?(Dictionary) ? Dictionary.new : Hash.new
292
+ if self.range_hash
293
+ @category_values = self.categories.inject(container) do |cont, cat|
294
+ cont[cat] = self.find_all &self.range_hash[cat]
295
+ cont
296
+ end
297
+ else
298
+ @category_values = self.categories.inject(container) do |cont, cat|
299
+ cont[cat] = self.find_all {|e| e == cat}
300
+ cont
301
+ end
302
+ end
303
+ end
304
+
244
305
  # When creating a range, what class will it be? Defaults to Range, but
245
306
  # other classes are sometimes useful.
246
307
  def range_class
@@ -210,7 +210,9 @@ module JustEnumerableStats #:nodoc:
210
210
  # For non-numeric values, returns a unique set,
211
211
  # ordered if possible.
212
212
  def categories
213
- if self.is_numeric?
213
+ if @categories
214
+ @categories
215
+ elsif self.is_numeric?
214
216
  self.range_instance.map
215
217
  else
216
218
  self.uniq.sort rescue self.uniq
@@ -234,8 +236,61 @@ module JustEnumerableStats #:nodoc:
234
236
  self.range_class
235
237
  end
236
238
 
239
+ # Takes a hash of arrays for categories
240
+ # If Facets happens to be loaded on the computer, this keeps the order
241
+ # of the categories straight.
242
+ def set_range(hash)
243
+ if defined?(Dictionary)
244
+ @range_hash = Dictionary.new
245
+ @range_hash.merge!(hash)
246
+ @categories = @range_hash.keys
247
+ else
248
+ @categories = hash.keys
249
+ @range_hash = hash
250
+ end
251
+ @categories
252
+ end
253
+
254
+ # The hash of lambdas that are used to categorize the enumerable.
255
+ attr_reader :range_hash
256
+
257
+ # The arguments needed to instantiate the custom-defined range class.
237
258
  attr_reader :range_class_args
238
259
 
260
+ # Counts each element where the block evaluates to true
261
+ # Example:
262
+ # a = [1,2,3]
263
+ # a.count_if {|e| e % 2 == 0}
264
+ def count_if(&block)
265
+ self.inject(0) do |s, e|
266
+ s += 1 if block.call(e)
267
+ s
268
+ end
269
+ end
270
+
271
+ # Returns a Hash or Dictionary (if available) for each category with a
272
+ # value as the set of matching values as an array.
273
+ # Because this is supposed to be lean (just enumerables), but this is an
274
+ # expensive call, I'm going to cache it and offer a parameter to reset
275
+ # the cache. So, call category_values(true) if you need to reset the
276
+ # cache.
277
+ def category_values(reset=false)
278
+ @category_values = nil if reset
279
+ return @category_values if @category_values
280
+ container = defined?(Dictionary) ? Dictionary.new : Hash.new
281
+ if self.range_hash
282
+ @category_values = self.categories.inject(container) do |cont, cat|
283
+ cont[cat] = self.find_all &self.range_hash[cat]
284
+ cont
285
+ end
286
+ else
287
+ @category_values = self.categories.inject(container) do |cont, cat|
288
+ cont[cat] = self.find_all {|e| e == cat}
289
+ cont
290
+ end
291
+ end
292
+ end
293
+
239
294
  # When creating a range, what class will it be? Defaults to Range, but
240
295
  # other classes are sometimes useful.
241
296
  def range_class
@@ -243,6 +243,42 @@ describe JustEnumerableStats::Stats do
243
243
  a.categories.should eql(a)
244
244
  end
245
245
 
246
+ it "should be able to set a range with a hash of lambdas" do
247
+ @a.set_range({
248
+ "<= 2" => lambda{ |e| e <= 2 },
249
+ "> 2" => lambda{ |e| e > 2 }
250
+ })
251
+ @a.categories.sort.should eql(["<= 2", "> 2"].sort)
252
+ end
253
+
254
+ it "should be able to get a hash of category values" do
255
+ @a.set_range({
256
+ "<= 2" => lambda{ |e| e <= 2 },
257
+ "> 2" => lambda{ |e| e > 2 }
258
+ })
259
+ @a.category_values["<= 2"].should eql([1,2])
260
+ @a.category_values["> 2"].should eql([3])
261
+ end
262
+
263
+ it "should be able to get category values with a regular range" do
264
+ @a.category_values[1].should eql([1])
265
+ @a.category_values[2].should eql([2])
266
+ @a.category_values[3].should eql([3])
267
+ end
268
+
269
+ it "should be able to get category values with a custom range" do
270
+ @a.set_range_class(FixedRange, 1, 3, 0.5)
271
+ @a.category_values[1.0].should eql([1])
272
+ @a.category_values[1.5].should eql([])
273
+ @a.category_values[2.0].should eql([2])
274
+ @a.category_values[2.5].should eql([])
275
+ @a.category_values[3.0].should eql([3])
276
+ end
277
+
278
+ it "should be able to count conditionally" do
279
+ @a.count_if {|e| e == 2}.should eql(1)
280
+ end
281
+
246
282
  it "should be able to instantiate a range" do
247
283
  @a.range_as_range.should eql(Range.new(1, 3))
248
284
  end
@@ -233,6 +233,42 @@ describe "JustEnumerableStats" do
233
233
  a.categories.should eql(a)
234
234
  end
235
235
 
236
+ it "should be able to set a range with a hash of lambdas" do
237
+ @a.set_range({
238
+ "<= 2" => lambda{ |e| e <= 2 },
239
+ "> 2" => lambda{ |e| e > 2 }
240
+ })
241
+ @a.categories.sort.should eql(["<= 2", "> 2"].sort)
242
+ end
243
+
244
+ it "should be able to get a hash of category values" do
245
+ @a.set_range({
246
+ "<= 2" => lambda{ |e| e <= 2 },
247
+ "> 2" => lambda{ |e| e > 2 }
248
+ })
249
+ @a.category_values["<= 2"].should eql([1,2])
250
+ @a.category_values["> 2"].should eql([3])
251
+ end
252
+
253
+ it "should be able to get category values with a regular range" do
254
+ @a.category_values[1].should eql([1])
255
+ @a.category_values[2].should eql([2])
256
+ @a.category_values[3].should eql([3])
257
+ end
258
+
259
+ it "should be able to get category values with a custom range" do
260
+ @a.set_range_class(FixedRange, 1, 3, 0.5)
261
+ @a.category_values[1.0].should eql([1])
262
+ @a.category_values[1.5].should eql([])
263
+ @a.category_values[2.0].should eql([2])
264
+ @a.category_values[2.5].should eql([])
265
+ @a.category_values[3.0].should eql([3])
266
+ end
267
+
268
+ it "should be able to count conditionally" do
269
+ @a.count_if {|e| e == 2}.should eql(1)
270
+ end
271
+
236
272
  it "should be able to instantiate a range with a block" do
237
273
  @a.range_as_range(&@inverse_matcher).should eql(Range.new(3, 1))
238
274
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: davidrichards-just_enumerable_stats
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Richards