Skip to content
Browse files

added support for averages

  • Loading branch information...
1 parent f7f657a commit f8a3257dd5b5a4a2972d401ffd25bfa9d9d44879 @hamin hamin committed Oct 10, 2011
Showing with 53 additions and 3 deletions.
  1. +15 −3 README.markdown
  2. +26 −0 lib/reduceable.rb
  3. +3 −0 spec/helpers.rb
  4. +9 −0 spec/reduceable_spec.rb
View
18 README.markdown
@@ -24,15 +24,16 @@ Here are some use cases
```ruby
# Count how many times each tag is used
Model.count_by(:tag, query = {})
-# Sum all the weights of the different types of wresters
-Model.sum_of(:weight, :wrester_type, query = {})
+# Sum all the weights of the different types of wrestlers
+Model.sum_of(:weight, :wrestler_type, query = {})
+# Find Average Weight of the different types of wrestlers
+Model.average_of(:weight, :wrestler_type, query = {})
```
Coming Soon
----------
+ mongoid support
+ Sum by composite index
-+ Averages
+ More Unit Tests :(
Installation
@@ -69,6 +70,9 @@ end
BlogPost.sum_of(:article_length, :categories).to_a.each do |x|
puts "You have written #{x['value']} characters in category #{x['_id']}"
end
+BlogPost.average_of(:article_length, :categories).to_a.each do |x|
+ puts "An article in category #{x['_id']} has an average of #{x['value']} characters"
+end
```
See example.rb
@@ -87,10 +91,18 @@ Test.count_by(:tags).to_a
# Sum up the sale_amounts per tag
Test.sum_of(:sale_amount, :tags).to_a
+# Find the average sale_amounts per tag
+Test.average_of(:sale_amount, :tags).to_a
+
# Sum up the sale_amounts per tag where tags contains 'book'
Test.sum_of(:sale_amount, :tags, {:tags => 'book'}).to_a
# you can optionally pass in a mongo query that limits the initial dataset being
# fed to the map function.
+
+# Find the average of sale_amounts per tag where tags contains 'book'
+Test.average_of(:sale_amount, :tags, {:tags => 'book'}).to_a
+# you can optionally pass in a mongo query that limits the initial dataset being
+# fed to the map function.
```
For such a small collection the speed benefits aren't present, but once you get to
View
26 lib/reduceable.rb
@@ -47,6 +47,32 @@ def sum_reduce
}
REDUCE
end
+
+ def average_of(property, index, query={})
+ collection = mr_collection_name("average_of_#{property}_by_#{index}", query)
+ map = average_map(property, index)
+ reduce = average_reduce
+ return build(collection, map, reduce, query).find
+ end
+ def average_map(property, index)
+ index = index.to_s if index.is_a? Symbol
+ if self.keys[index].type == Array
+ "function(){var amount = this.#{property};this.#{index}.forEach(function(value){emit(value, amount);});}"
+ else
+ "function(){emit(this.#{index}, this.#{property});}"
+ end
+ end
+ def average_reduce
+ <<-REDUCE
+ function(key, values) {
+ var total = 0;
+ for (var i=0; i<values.length; i++){
+ total += values[i];
+ }
+ return (total / values.length);
+ }
+ REDUCE
+ end
def count_by(index, query={})
collection = mr_collection_name("count_by_#{index}", query)
View
3 spec/helpers.rb
@@ -18,6 +18,9 @@ def count_answers
def sum_answers
{'book'=>200.0, 'fiction'=>80.0, 'music'=>82.0, 'fantasy'=>80.0, 'non-fiction'=>40.0, 'rock'=>20.5, 'pop'=>20.5, 'classical'=>20.5, 'alternative'=>20.5}
end
+ def average_answers
+ {'book'=>40.0, 'fiction'=>40.0, 'music'=>20.5, 'fantasy'=>40.0, 'non-fiction'=>40.0, 'rock'=>20.5, 'pop'=>20.5, 'classical'=>20.5, 'alternative'=>20.5}
+ end
def load_data
# add some data
Sale.create(:date => Date.today, :tags => ['book', 'fiction'], :sale_amount => 40)
View
9 spec/reduceable_spec.rb
@@ -38,4 +38,13 @@
answer.should eql(value['value']), "Number of #{id}s calculated. Expected #{value['value']} got #{answer}"
end
end
+ it "should be able to average" do
+ values = Sale.average_of(:sale_amount,:tags).to_a
+ answers = average_answers
+ values.each do |value|
+ id = value['_id']
+ answer = answers[id]
+ answer.should eql(value['value']), "Number of #{id}s calculated. Expected #{value['value']} got #{answer}"
+ end
+ end
end

0 comments on commit f8a3257

Please sign in to comment.
Something went wrong with that request. Please try again.