ms_pivot 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.md +89 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/lib/ms_pivot/measure.rb +64 -0
- data/lib/ms_pivot/row.rb +38 -0
- data/lib/ms_pivot/table.rb +121 -0
- data/lib/ms_pivot.rb +3 -0
- data/ms_pivot.gemspec +59 -0
- data/spec/measure_spec.rb +100 -0
- data/spec/pivot_spec.rb +104 -0
- data/spec/table_spec.rb +72 -0
- metadata +90 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 VisFleet Ltd.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Ms Pivot
|
2
|
+
by [vWorkApp](http://www.vworkapp.com)
|
3
|
+
|
4
|
+
A little gem to help you pivot your arrays.
|
5
|
+
|
6
|
+
my_array = [
|
7
|
+
["ProductA", "AU", 1],
|
8
|
+
["ProductA", "AU", 2],
|
9
|
+
["ProductB", "NZ", 3],
|
10
|
+
["ProductB", "US", 4],
|
11
|
+
]
|
12
|
+
|
13
|
+
pv = MsPivot::Table.new(my_array, 0, 1, MsPivot::SUM)
|
14
|
+
puts pv.inspect
|
15
|
+
|
16
|
+
# AU NZ US
|
17
|
+
# ProductA 3 - -
|
18
|
+
# ProductB - 3 4
|
19
|
+
|
20
|
+
## Install
|
21
|
+
|
22
|
+
sudo gem install ms_pivot
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
- Specify which items (indicies) to use as row and column headers. All remaining items must have a measure specified.
|
27
|
+
|
28
|
+
my_array = [
|
29
|
+
["ProductA", "AU", 1, "x"],
|
30
|
+
["ProductA", "AU", 2, "t"],
|
31
|
+
["ProductB", "NZ", 3, "z"],
|
32
|
+
["ProductB", "US", 4, "a"],
|
33
|
+
]
|
34
|
+
pv = MsPivot::Table.new(my_array, 0, 1, MsPivot::SUM, MsPivot::COUNT)
|
35
|
+
|
36
|
+
Item 0 is used as the row headers, item 1 is used as the column headers, item 2 and 3 are aggregated (summed and counted respectively).
|
37
|
+
|
38
|
+
- Use of of the following built-in measures (i.e. methods to aggregate the grouped data)
|
39
|
+
|
40
|
+
- MSPivot::SUM
|
41
|
+
- MSPivot::COUNT
|
42
|
+
- MSPivot::AVG
|
43
|
+
- MSPivot::MIN
|
44
|
+
- MSPivot::MAX
|
45
|
+
- MSPivot::APPEND (builds an array)
|
46
|
+
|
47
|
+
- Or specify your own measure function
|
48
|
+
|
49
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::Measure.new { |current_value, new_value|
|
50
|
+
# sum of squares
|
51
|
+
current_value ||= 0
|
52
|
+
current_value + (new_value ** 2)
|
53
|
+
end
|
54
|
+
|
55
|
+
- Let Ms Pivot work out automatically what the column headers are from the data (in which case it'll order them alphabetically) or tell it explicitly what columns you want
|
56
|
+
|
57
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM)
|
58
|
+
pv.column_headers = ["AU", "US", "UK"]
|
59
|
+
|
60
|
+
- Get the results out as an array
|
61
|
+
|
62
|
+
a = pv.column_headers
|
63
|
+
b = pv.to_a
|
64
|
+
|
65
|
+
# a = ["AU", "NZ", "US"]
|
66
|
+
# b = [
|
67
|
+
# ["ProductA", [3, 2], [], []],
|
68
|
+
# ["ProductB", [], [3, 1], [4, 1]]
|
69
|
+
# ]
|
70
|
+
|
71
|
+
- Or iterate over them
|
72
|
+
|
73
|
+
pv.each do |row_header, columns| do
|
74
|
+
columns.each do |col_header, values| do
|
75
|
+
values.each do |value|
|
76
|
+
// do stuff
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
See /spec/pivot_spec.rb for some examples of Ms Pivot being used.
|
82
|
+
|
83
|
+
## Todo
|
84
|
+
|
85
|
+
- Write RDOC
|
86
|
+
|
87
|
+
## Copyright
|
88
|
+
|
89
|
+
Copyright (c) 2010 VisFleet. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ms_pivot"
|
8
|
+
gem.summary = %Q{A little gem to help you pivot your arrays}
|
9
|
+
gem.description = %Q{A little gem to help you pivot your arrays}
|
10
|
+
gem.email = "aisha.fenton@visfleet.com"
|
11
|
+
gem.homepage = "http://github.com/aishafenton/ms_pivot"
|
12
|
+
gem.authors = ["Aisha Fenton"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 2.3"
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'rcov/rcovtask'
|
22
|
+
Rcov::RcovTask.new do |test|
|
23
|
+
test.libs << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
rescue LoadError
|
28
|
+
task :rcov do
|
29
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'rspec/core/rake_task'
|
35
|
+
RSpec::Core::RakeTask.new(:spec)
|
36
|
+
rescue LoadError
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
42
|
+
|
43
|
+
rdoc.rdoc_dir = 'rdoc'
|
44
|
+
rdoc.title = "ms_pivot #{version}"
|
45
|
+
rdoc.rdoc_files.include('README*')
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module MsPivot
|
2
|
+
|
3
|
+
##
|
4
|
+
# The table to pivot
|
5
|
+
#
|
6
|
+
class Measure
|
7
|
+
|
8
|
+
def initialize(finalize_func = nil, &measure_func)
|
9
|
+
@measure_func = measure_func
|
10
|
+
@finalize_func = finalize_func
|
11
|
+
end
|
12
|
+
|
13
|
+
def measure(current_value, new_value)
|
14
|
+
@measure_func.call(current_value, new_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def finalize(value)
|
18
|
+
return value if @finalize_func.nil?
|
19
|
+
@finalize_func.call(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
# Built-in measures
|
25
|
+
APPEND = Measure.new do |current_value, new_value|
|
26
|
+
current_value ||= []
|
27
|
+
current_value << new_value
|
28
|
+
end
|
29
|
+
|
30
|
+
SUM = Measure.new do |current_value, new_value|
|
31
|
+
current_value ||= 0
|
32
|
+
current_value + new_value
|
33
|
+
end
|
34
|
+
|
35
|
+
MIN = Measure.new do |current_value, new_value|
|
36
|
+
if current_value.nil?
|
37
|
+
new_value
|
38
|
+
else
|
39
|
+
new_value < current_value ? new_value : current_value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
MAX = Measure.new do |current_value, new_value|
|
44
|
+
if current_value.nil?
|
45
|
+
new_value
|
46
|
+
else
|
47
|
+
new_value > current_value ? new_value : current_value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
COUNT = Measure.new do |current_value, new_value|
|
52
|
+
current_value ||= 0
|
53
|
+
current_value + 1
|
54
|
+
end
|
55
|
+
|
56
|
+
AVG = Measure.new(Proc.new { |value| value[1] / value[0].to_f }) do |current_value, new_value|
|
57
|
+
current_value ||= Struct.new(:count, :sum).new(0,0)
|
58
|
+
current_value.count += 1
|
59
|
+
current_value.sum += new_value
|
60
|
+
current_value
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
data/lib/ms_pivot/row.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module MsPivot
|
2
|
+
|
3
|
+
##
|
4
|
+
# The table to pivot
|
5
|
+
#
|
6
|
+
class Row
|
7
|
+
|
8
|
+
attr_accessor :column_headers
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@values = Hash.new { |h,k| h[k] = [] }
|
12
|
+
end
|
13
|
+
|
14
|
+
def each
|
15
|
+
@column_headers.each do |col_header|
|
16
|
+
yield col_header, @values[col_header]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(col_header, *values)
|
21
|
+
@values[col_header] = values
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](col_header)
|
25
|
+
@values[col_header]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_a
|
29
|
+
result = []
|
30
|
+
self.each do |col_header, values|
|
31
|
+
result << values
|
32
|
+
end
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module MsPivot
|
2
|
+
|
3
|
+
##
|
4
|
+
# The table to pivot
|
5
|
+
#
|
6
|
+
class Table
|
7
|
+
|
8
|
+
attr_accessor :column_headers
|
9
|
+
|
10
|
+
def initialize(orig_array, row_index, col_index, *measures)
|
11
|
+
@data = {}
|
12
|
+
@column_headers = nil
|
13
|
+
@row_index = row_index
|
14
|
+
@col_index = col_index
|
15
|
+
@measures = measures
|
16
|
+
@orig_array = orig_array
|
17
|
+
@orig_array
|
18
|
+
|
19
|
+
compile(@orig_array)
|
20
|
+
end
|
21
|
+
|
22
|
+
def column_headers=(column_headers)
|
23
|
+
@column_headers = column_headers
|
24
|
+
compile(@orig_array)
|
25
|
+
end
|
26
|
+
|
27
|
+
def each
|
28
|
+
@data.each do |row_header, columns|
|
29
|
+
yield row_header, columns
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def row_headers
|
34
|
+
@data.keys
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_a
|
38
|
+
result = []
|
39
|
+
self.each do |row_header, columns|
|
40
|
+
result << Array(row_header) + columns.to_a
|
41
|
+
end
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
result = "\t"
|
47
|
+
self.column_headers.each do |header|
|
48
|
+
result << "\t#{header}"
|
49
|
+
end
|
50
|
+
result << "\n"
|
51
|
+
|
52
|
+
self.each do |row_header, columns|
|
53
|
+
result << row_header.to_s
|
54
|
+
columns.each do |col_header, values|
|
55
|
+
result << "\t" + (values.empty? ? "-" : "#{values.join(",")}")
|
56
|
+
end
|
57
|
+
result << "\n"
|
58
|
+
end
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def clear
|
65
|
+
@data = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
def compile(orig_array)
|
69
|
+
clear
|
70
|
+
|
71
|
+
column_headers = {}
|
72
|
+
|
73
|
+
# group rows
|
74
|
+
orig_array.each do |row|
|
75
|
+
row_header = row[@row_index]
|
76
|
+
col_header = row[@col_index]
|
77
|
+
|
78
|
+
row = strip_headers(row, @row_index, @col_index)
|
79
|
+
@data[row_header] ||= Row.new
|
80
|
+
|
81
|
+
# group cols, and run measures on remaining rows
|
82
|
+
data_row_idx = 0
|
83
|
+
row.size.times do |i|
|
84
|
+
|
85
|
+
if row.size != @measures.size
|
86
|
+
raise "You need to have a measure for each row (excluding the row and column headers). \
|
87
|
+
You have #{@measures.size} measures, and #{row.size} rows (excluding headers)"
|
88
|
+
end
|
89
|
+
|
90
|
+
@data[row_header][col_header][i] = @measures[i].measure(@data[row_header][col_header][i], row[i])
|
91
|
+
end
|
92
|
+
|
93
|
+
column_headers[col_header] = true
|
94
|
+
end
|
95
|
+
|
96
|
+
# set column_headers, now that we know what they are
|
97
|
+
@column_headers ||= column_headers.keys.sort
|
98
|
+
@data.each_value { |row| row.column_headers = @column_headers }
|
99
|
+
|
100
|
+
# Invoke finalize methods on measures
|
101
|
+
self.each do |header, columns|
|
102
|
+
columns.each do |header, values|
|
103
|
+
values.each_with_index do |value, i|
|
104
|
+
values[i] = @measures[i].finalize(value)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def strip_headers(row, *indexes)
|
112
|
+
result = []
|
113
|
+
row.each_with_index do |cell, i|
|
114
|
+
result << cell unless indexes.include?(i)
|
115
|
+
end
|
116
|
+
result
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
data/lib/ms_pivot.rb
ADDED
data/ms_pivot.gemspec
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ms_pivot}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Aisha Fenton"]
|
12
|
+
s.date = %q{2010-12-16}
|
13
|
+
s.description = %q{A little gem to help you pivot your arrays}
|
14
|
+
s.email = %q{aisha.fenton@visfleet.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.md",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/ms_pivot.rb",
|
27
|
+
"lib/ms_pivot/measure.rb",
|
28
|
+
"lib/ms_pivot/row.rb",
|
29
|
+
"lib/ms_pivot/table.rb",
|
30
|
+
"ms_pivot.gemspec",
|
31
|
+
"spec/measure_spec.rb",
|
32
|
+
"spec/pivot_spec.rb",
|
33
|
+
"spec/table_spec.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{http://github.com/aishafenton/ms_pivot}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.6}
|
39
|
+
s.summary = %q{A little gem to help you pivot your arrays}
|
40
|
+
s.test_files = [
|
41
|
+
"spec/measure_spec.rb",
|
42
|
+
"spec/pivot_spec.rb",
|
43
|
+
"spec/table_spec.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
s.add_development_dependency(%q<rspec>, [">= 2.3"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<rspec>, [">= 2.3"])
|
54
|
+
end
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<rspec>, [">= 2.3"])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'lib/ms_pivot'
|
2
|
+
|
3
|
+
describe MsPivot::Measure do
|
4
|
+
|
5
|
+
it "supports a SUM measure" do
|
6
|
+
orig_array = [
|
7
|
+
["ProductA", "NZ", 1],
|
8
|
+
["ProductA", "NZ", 2],
|
9
|
+
["ProductB", "NZ", 3],
|
10
|
+
]
|
11
|
+
|
12
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM)
|
13
|
+
|
14
|
+
pv.to_a.should == [
|
15
|
+
["ProductA", [3]],
|
16
|
+
["ProductB", [3]]
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "supports a MIN measure" do
|
21
|
+
orig_array = [
|
22
|
+
["ProductA", "NZ", -100],
|
23
|
+
["ProductA", "NZ", 0],
|
24
|
+
["ProductB", "NZ", 1],
|
25
|
+
["ProductB", "NZ", 3],
|
26
|
+
]
|
27
|
+
|
28
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::MIN)
|
29
|
+
|
30
|
+
pv.to_a.should == [
|
31
|
+
["ProductA", [-100]],
|
32
|
+
["ProductB", [1]]
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "supports a MAX measure" do
|
37
|
+
orig_array = [
|
38
|
+
["ProductA", "NZ", -2],
|
39
|
+
["ProductA", "NZ", 2],
|
40
|
+
["ProductB", "NZ", 1],
|
41
|
+
["ProductB", "NZ", 3],
|
42
|
+
]
|
43
|
+
|
44
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::MAX)
|
45
|
+
|
46
|
+
pv.to_a.should == [
|
47
|
+
["ProductA", [2]],
|
48
|
+
["ProductB", [3]]
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "supports a COUNT measure" do
|
53
|
+
|
54
|
+
orig_array = [
|
55
|
+
["ProductA", "NZ", "z"],
|
56
|
+
["ProductA", "NZ", "z"],
|
57
|
+
["ProductA", "US", "z"],
|
58
|
+
["ProductB", "NZ", "z"],
|
59
|
+
["ProductB", "US", "z"],
|
60
|
+
]
|
61
|
+
|
62
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::COUNT)
|
63
|
+
|
64
|
+
pv.to_a.should == [
|
65
|
+
["ProductA", [2], [1]],
|
66
|
+
["ProductB", [1], [1]]
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "supports a AVG measure" do
|
71
|
+
orig_array = [
|
72
|
+
["ProductA", "NZ", 2],
|
73
|
+
["ProductA", "NZ", 3],
|
74
|
+
["ProductA", "US", 3],
|
75
|
+
["ProductB", "US", 1],
|
76
|
+
["ProductB", "US", 1],
|
77
|
+
]
|
78
|
+
|
79
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::AVG)
|
80
|
+
|
81
|
+
pv.to_a.should == [
|
82
|
+
["ProductA", [2.5], [3]],
|
83
|
+
["ProductB", [], [1]]
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "supports an APPEND measure" do
|
88
|
+
orig_array = [
|
89
|
+
["ProductA", "NZ", "x"],
|
90
|
+
["ProductA", "NZ", "y"],
|
91
|
+
]
|
92
|
+
|
93
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::APPEND)
|
94
|
+
|
95
|
+
pv.to_a.should == [
|
96
|
+
["ProductA", [["x", "y"]]],
|
97
|
+
]
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
data/spec/pivot_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'lib/ms_pivot'
|
2
|
+
|
3
|
+
describe "Pivot Features!" do
|
4
|
+
|
5
|
+
it "lets you pivot an array" do
|
6
|
+
|
7
|
+
orig_array = [
|
8
|
+
["ProductA", "AU", 1],
|
9
|
+
["ProductA", "NZ", 2],
|
10
|
+
["ProductA", "US", 3],
|
11
|
+
["ProductB", "AU", 4],
|
12
|
+
["ProductB", "NZ", 5],
|
13
|
+
["ProductB", "US", 6],
|
14
|
+
]
|
15
|
+
|
16
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM)
|
17
|
+
|
18
|
+
pv.column_headers.should == [
|
19
|
+
"AU", "NZ", "US"
|
20
|
+
]
|
21
|
+
|
22
|
+
pv.to_a.should == [
|
23
|
+
["ProductA", [1], [2], [3]],
|
24
|
+
["ProductB", [4], [5], [6]]
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "works with multiple 'measures'" do
|
29
|
+
|
30
|
+
orig_array = [
|
31
|
+
["ProductA", "NZ", 1, "a", "x"],
|
32
|
+
["ProductA", "US", 2, "b", "y"],
|
33
|
+
["ProductA", "US", 3, "b", "z"],
|
34
|
+
["ProductB", "NZ", 4, "a", "x"],
|
35
|
+
["ProductB", "NZ", 5, "b", "y"],
|
36
|
+
["ProductB", "US", 6, "b", "z"],
|
37
|
+
]
|
38
|
+
|
39
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM, MsPivot::COUNT, MsPivot::APPEND)
|
40
|
+
|
41
|
+
pv.to_a.should == [
|
42
|
+
["ProductA", [1, 1, ["x"]], [5, 2, ["y", "z"]]],
|
43
|
+
["ProductB", [9, 2, ["x", "y"]], [6, 1, ["z"]]]
|
44
|
+
]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "works when not all rows have all columns" do
|
48
|
+
orig_array = [
|
49
|
+
["ProductA", "NZ", 1],
|
50
|
+
["ProductA", "NZ", 1],
|
51
|
+
["ProductB", "US", 4],
|
52
|
+
]
|
53
|
+
|
54
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM)
|
55
|
+
|
56
|
+
pv.to_a.should == [
|
57
|
+
["ProductA", [2], []],
|
58
|
+
["ProductB", [], [4]]
|
59
|
+
]
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
it "lets you specify your own measures" do
|
64
|
+
orig_array = [
|
65
|
+
["ProductA", "NZ", 1, "a"],
|
66
|
+
["ProductA", "NZ", 2, "b"],
|
67
|
+
["ProductB", "NZ", 3, "c"],
|
68
|
+
["ProductB", "US", 4, "d"],
|
69
|
+
]
|
70
|
+
|
71
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::Measure.new { |current_value, new_value|
|
72
|
+
# sum of squares
|
73
|
+
current_value ||= 0
|
74
|
+
current_value + (new_value ** 2)
|
75
|
+
}, MsPivot::Measure.new { |current_value, new_value|
|
76
|
+
# concat string
|
77
|
+
current_value ||= ""
|
78
|
+
current_value + new_value
|
79
|
+
})
|
80
|
+
|
81
|
+
pv.to_a.should == [
|
82
|
+
["ProductA", [5, "ab"], []],
|
83
|
+
["ProductB", [9, "c"], [16, "d"]]
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "lets you specify the column headers (and their order)" do
|
88
|
+
orig_array = [
|
89
|
+
["ProductA", "NZ", 1],
|
90
|
+
["ProductA", "UK", 2],
|
91
|
+
["ProductB", "US", 3],
|
92
|
+
["ProductB", "AU", 4],
|
93
|
+
]
|
94
|
+
|
95
|
+
pv = MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM)
|
96
|
+
pv.column_headers = ["AU", "US", "UK"]
|
97
|
+
|
98
|
+
pv.to_a.should == [
|
99
|
+
["ProductA", [], [], [2]],
|
100
|
+
["ProductB", [4], [3], []]
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
data/spec/table_spec.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'lib/ms_pivot'
|
2
|
+
|
3
|
+
describe MsPivot::Table, "with a single measure" do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
orig_array = [
|
7
|
+
["ProductA", "NZ", 1],
|
8
|
+
["ProductA", "US", 2],
|
9
|
+
["ProductB", "NZ", 3],
|
10
|
+
]
|
11
|
+
MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM)
|
12
|
+
end
|
13
|
+
|
14
|
+
its(:row_headers) { should == ["ProductA", "ProductB"] }
|
15
|
+
|
16
|
+
context "when calling each_row" do
|
17
|
+
|
18
|
+
it "returns the row headers" do
|
19
|
+
headers = []
|
20
|
+
subject.each { |header, columns| headers << header }
|
21
|
+
headers.should == ["ProductA", "ProductB"]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns the row values" do
|
25
|
+
columns = []
|
26
|
+
subject.each { |header, cols| columns << cols }
|
27
|
+
columns[0].to_a.should == [[1], [2]]
|
28
|
+
columns[1].to_a.should == [[3], []]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
its(:to_a) { should == [ ["ProductA", [1], [2]], ["ProductB", [3], []] ] }
|
34
|
+
|
35
|
+
it "remeasures when the column headers are explicitly set" do
|
36
|
+
subject.column_headers = ["US", "NZ", "AU"]
|
37
|
+
|
38
|
+
subject.column_headers.should == ["US", "NZ", "AU"]
|
39
|
+
subject.to_a.should == [ ["ProductA", [2], [1], []], ["ProductB", [], [3], []] ]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "works when rows are missing column headers" do
|
43
|
+
subject.column_headers = ["US", "XX", "YY"]
|
44
|
+
subject.to_a.should == [ ["ProductA", [2], [], []], ["ProductB", [], [], []] ]
|
45
|
+
end
|
46
|
+
|
47
|
+
its(:inspect) do
|
48
|
+
should == <<-EOL
|
49
|
+
\t\tNZ\tUS
|
50
|
+
ProductA\t1\t2
|
51
|
+
ProductB\t3\t-
|
52
|
+
EOL
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe MsPivot::Table, "with mulitple measures" do
|
58
|
+
|
59
|
+
subject do
|
60
|
+
orig_array = [
|
61
|
+
["ProductA", "NZ", 1, "x", 2],
|
62
|
+
["ProductA", "US", 2, "y", 3],
|
63
|
+
["ProductB", "NZ", 3, "z", 4],
|
64
|
+
]
|
65
|
+
MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM, MsPivot::COUNT, MsPivot::MIN)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "throws a exception when there aren't enough measures specified" do
|
69
|
+
expect{ MsPivot::Table.new(orig_array, 0, 1, MsPivot::SUM) }.to raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ms_pivot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Aisha Fenton
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-16 00:00:00 +13:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 3
|
30
|
+
version: "2.3"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
description: A little gem to help you pivot your arrays
|
34
|
+
email: aisha.fenton@visfleet.com
|
35
|
+
executables: []
|
36
|
+
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- LICENSE
|
41
|
+
- README.md
|
42
|
+
files:
|
43
|
+
- .document
|
44
|
+
- .gitignore
|
45
|
+
- LICENSE
|
46
|
+
- README.md
|
47
|
+
- Rakefile
|
48
|
+
- VERSION
|
49
|
+
- lib/ms_pivot.rb
|
50
|
+
- lib/ms_pivot/measure.rb
|
51
|
+
- lib/ms_pivot/row.rb
|
52
|
+
- lib/ms_pivot/table.rb
|
53
|
+
- ms_pivot.gemspec
|
54
|
+
- spec/measure_spec.rb
|
55
|
+
- spec/pivot_spec.rb
|
56
|
+
- spec/table_spec.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/aishafenton/ms_pivot
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --charset=UTF-8
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.3.6
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: A little gem to help you pivot your arrays
|
87
|
+
test_files:
|
88
|
+
- spec/measure_spec.rb
|
89
|
+
- spec/pivot_spec.rb
|
90
|
+
- spec/table_spec.rb
|