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.
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/lib/group_by_hash.rb +41 -0
- data/spec/group_by_hash_spec.rb +92 -0
- data/spec/spec_helper.rb +4 -0
- metadata +62 -0
data/Rakefile
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
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
|