group_by_hash 0.1.0

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,32 @@
1
+ task :default => :spec
2
+ task :test => :spec
3
+
4
+ desc "Run specs"
5
+ task :spec do
6
+ exec "spec spec/"
7
+ end
8
+
9
+ desc "Build a gem"
10
+ task :gem => [ :gemspec, :build ]
11
+
12
+ desc "Run specs"
13
+ task :spec do
14
+ exec "spec spec/"
15
+ end
16
+
17
+ begin
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |gemspec|
20
+ gemspec.name = "group_by_hash"
21
+ gemspec.summary = "ruby hash with a sql-esque group by method"
22
+ gemspec.description = <<END
23
+ A ruby hash with a sql-esque group by method for when you can't do a group by in SQL because it is too complicated
24
+ END
25
+ gemspec.email = "paulboone@mindbucket.com"
26
+ gemspec.homepage = "http://github.com/paulboone/group_by_hash"
27
+ gemspec.authors = ["Paul Boone"]
28
+ end
29
+ rescue LoadError
30
+ warn "Jeweler not available. Install it with:"
31
+ warn "gem install jeweler"
32
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,41 @@
1
+ =begin
2
+
3
+ =end
4
+ class GroupByHash < Hash
5
+
6
+ #
7
+ # group_by_fields is a list of symbols that MUST be available on datasets passed in via <<
8
+ # blk is a block that returns a hash where each field will be += together
9
+ def initialize(group_by_fields, &blk)
10
+ @group_by_fields = group_by_fields
11
+ @blk = blk
12
+ end
13
+
14
+ def flatten
15
+ a = []
16
+ each_pair do |k,v|
17
+ a << k.merge(v)
18
+ end
19
+ a
20
+ end
21
+
22
+ def <<(array_or_hash)
23
+ h = array_or_hash.clone
24
+ key = h.reject{|k,v| ! @group_by_fields.include?(k)}
25
+ val = h.reject{|k,v| @group_by_fields.include?(k)}
26
+ if self[key]
27
+ self[key] = add_hash_fields(self[key],@blk.call(val))
28
+ else
29
+ self[key] = @blk.call(val)
30
+ end
31
+ self
32
+ end
33
+
34
+ def add_hash_fields(h1,h2)
35
+ h = {}
36
+ h1.each_pair do |k,v|
37
+ h[k] = v + h2[k]
38
+ end
39
+ h
40
+ end
41
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe GroupByHash do
4
+ it "should be defineable" do
5
+ g = GroupByHash.new([:col1,:col2,:col3]) {|h| {:count => 1, :random_sum => rand(500)}}
6
+ end
7
+
8
+ describe "add_hash_fields" do
9
+ it "should handle simple addition" do
10
+ GroupByHash.new(nil).add_hash_fields({:count => 5},{:count => 25}).should == {:count => 30}
11
+ end
12
+ end
13
+
14
+ describe "when flattening" do
15
+ before(:each) do
16
+ @g = GroupByHash.new([:col1]) {|h| {:count => 1}}
17
+ end
18
+
19
+ it "flattening and empty hash should == []" do
20
+ @g.flatten.should == []
21
+ end
22
+
23
+ it "adding one row" do
24
+ @g << {:col1 => 'disc1'}
25
+ fg = @g.flatten
26
+ fg[0].has_key?(:col1).should == true
27
+ fg[0].has_key?(:count).should == true
28
+ fg[0][:col1].should == 'disc1'
29
+ fg[0][:count].should == 1
30
+ end
31
+
32
+ it "adding three rows across two keys" do
33
+ @g << {:col1 => 'disc1'}
34
+ @g << {:col1 => 'disc2'}
35
+ @g << {:col1 => 'disc1'}
36
+ fg = @g.flatten
37
+ fg.size.should == 2
38
+ fg.each do |r|
39
+ if r[:col1] == 'disc1'
40
+ r[:count].should == 2
41
+ elsif r[:col1] == 'disc2'
42
+ r[:count].should == 1
43
+ else
44
+ fail
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ it "can use extra columns to calculate values" do
51
+ @g = GroupByHash.new([:col1]) {|h| {:units => h[:quantity]}}
52
+ @g << {:col1 => 1, :quantity => 4}
53
+ @g << {:col1 => 1, :quantity => 2}
54
+ @g << {:col1 => 1, :quantity => 5}
55
+ @g[{:col1 => 1}][:units].should == 11
56
+ end
57
+
58
+ describe "with col1/col2/col3 count" do
59
+ before(:each) do
60
+ @g = GroupByHash.new([:col1,:col2,:col3]) {|h| {:count => 1}}
61
+ end
62
+
63
+ describe "<<" do
64
+ it "should add hash key with only defined cols" do
65
+ @g << {:col1 => 1,:col2 => 2,:col3 => 3, :extra_col1 => 123}
66
+ @g.size.should == 1
67
+ @g.has_key?({:col1 => 1,:col2 => 2,:col3 => 3}).should == true
68
+ end
69
+
70
+ it "should add value with only block-defined cols" do
71
+ @g << {:col1 => 1,:col2 => 2,:col3 => 3, :extra_col1 => 123}
72
+ @g.size.should == 1
73
+ @g[{:col1 => 1,:col2 => 2,:col3 => 3}].should == {:count => 1}
74
+ end
75
+ end
76
+
77
+
78
+ it "groups right" do
79
+ @g << {:col1 => 1,:col2 => 2,:col3 => 3, :extra_col1 => 123}
80
+ @g << {:col1 => 1,:col2 => 2,:col3 => 3, :extra_col1 => 123}
81
+ @g << {:col1 => 1,:col2 => 2,:col3 => 3, :extra_col1 => 123}
82
+ @g << {:col1 => 1,:col2 => 2,:col3 => 5, :extra_col1 => 123}
83
+ @g << {:col1 => 1,:col2 => 4,:col3 => 5, :extra_col1 => 123}
84
+ @g.has_key?({:col1 => 1,:col2 => 2,:col3 => 3})
85
+ @g[{:col1 => 1,:col2 => 2,:col3 => 3}].should == {:count => 3}
86
+ @g.has_key?({:col1 => 1,:col2 => 2,:col3 => 5})
87
+ @g[{:col1 => 1,:col2 => 2,:col3 => 5}].should == {:count => 1}
88
+ @g.has_key?({:col1 => 1,:col2 => 4,:col3 => 5})
89
+ @g[{:col1 => 1,:col2 => 4,:col3 => 5}].should == {:count => 1}
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'group_by_hash'
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: group_by_hash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Boone
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-07-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: |
17
+ A ruby hash with a sql-esque group by method for when you can't do a group by in SQL because it is too complicated
18
+
19
+ email: paulboone@mindbucket.com
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ extra_rdoc_files: []
25
+
26
+ files:
27
+ - Rakefile
28
+ - VERSION
29
+ - lib/group_by_hash.rb
30
+ - spec/group_by_hash_spec.rb
31
+ - spec/spec_helper.rb
32
+ has_rdoc: true
33
+ homepage: http://github.com/paulboone/group_by_hash
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --charset=UTF-8
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.3.5
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: ruby hash with a sql-esque group by method
60
+ test_files:
61
+ - spec/group_by_hash_spec.rb
62
+ - spec/spec_helper.rb