Permalink
Browse files

Operation support for pivot method.

* Table#pivot now supports operations through the :operation option. Supported
  operations are :first, :sum and :count. If an operation is not specified,
  the default is :first; this way current apps that rely on this method should
  not break since :first was the original implementation.
* Removed old summation test. Added new tests for each operation.
* Updated docs.
  • Loading branch information...
Bob Nadler Bob Nadler
Bob Nadler authored and Bob Nadler committed Mar 30, 2010
1 parent 9fd92cd commit 333a43e3f8ab1e10c36edbb8ea540fb65bacad5b
Showing with 48 additions and 11 deletions.
  1. +21 −1 lib/ruport/data/table.rb
  2. +27 −10 test/table_pivot_test.rb
View
@@ -32,6 +32,7 @@ def initialize(table, group_col, pivot_col, summary_col, options = {})
@pivot_column = pivot_col
@summary_column = summary_col
@pivot_order = options[:pivot_order]
@operation = options[:operation] || :first
end
def convert_row_order_to_group_order(row_order_spec)
@@ -69,6 +70,19 @@ def group_column_entries
@table.map {|row| row[@group_column]}.uniq
end
def perform_operation(rows)
case @operation
when :first
return rows.first && rows.first[@summary_column]
when :sum
return rows && rows.inject(0) { |sum,row| sum+row[@summary_column] }
when :count
return rows && rows.length
else
raise ArgumentError, "Unknown pivot operation (#{@operation})"
end
end
def to_table
result = Table()
result.add_column(@group_column)
@@ -79,7 +93,7 @@ def to_table
outer_group = outer_grouping[outer_group_name]
pivot_values = pivoted_columns.inject({}) do |hsh, e|
matching_rows = outer_group.rows_with(@pivot_column => e)
hsh[e] = matching_rows && matching_rows.inject(0) { |sum,row| sum + row[@summary_column] }
hsh[e] = perform_operation(matching_rows)
hsh
end
result << [outer_group_name] + pivoted_columns.map {|e|
@@ -113,6 +127,12 @@ def to_table
# first argument. This wart will likely
# be fixed in a future version.
#
# <b><tt>:operation</tt></b>:: The operation to perform on
# <tt>:values</tt> column. Supported
# operations are <tt>:first</tt>,
# <tt>:sum</tt> and <tt>:count</tt>. If
# not specified, the default is <tt>:first</tt>.
#
# Example:
#
# Given a table <em>my_table</em>:
View
@@ -133,19 +133,36 @@ def test_preserves_ordering_on_calculated_column_with_proc_pivot_order
end
class TablePivotaggregationTest < Test::Unit::TestCase
class TablePivotOperationTest < Test::Unit::TestCase
def setup
table = Table('Region', 'Product', 'Units Sold')
table << ['North','Widget',5]
table << ['North','Widget',10]
table << ['South','Gadget',2]
table << ['South','Gadget',4]
@pivoted = table.pivot('Product', :group_by => 'Region', :values => 'Units Sold')
@table = Table('Region', 'Product', 'Units Sold')
@table << ['North','Widget',5]
@table << ['North','Widget',10]
@table << ['South','Gadget',2]
@table << ['South','Gadget',4]
end
def test_produces_correct_full_table_with_sum
def test_performs_operation_first
expected = Table("Region","Gadget","Widget") { |t| t << ["North",nil,5] << ["South",2,nil] }
pivoted = @table.pivot('Product', :group_by => 'Region', :values => 'Units Sold', :operation => :first)
assert_equal(expected, pivoted)
end
def test_performs_operation_count
expected = Table("Region","Gadget","Widget") { |t| t << ["North",0,2] << ["South",2,0] }
pivoted = @table.pivot('Product', :group_by => 'Region', :values => 'Units Sold', :operation => :count)
assert_equal(expected, pivoted)
end
def test_performs_operation_sum
expected = Table("Region","Gadget","Widget") { |t| t << ["North",0,15] << ["South",6,0] }
assert_equal(expected, @pivoted)
pivoted = @table.pivot('Product', :group_by => 'Region', :values => 'Units Sold', :operation => :sum)
assert_equal(expected, pivoted)
end
def test_invalid_operation_causes_exception
assert_raise ArgumentError do
@table.pivot('Product', :group_by => 'Region', :values => 'Units Sold', :operation => :foo)
end
end
end

0 comments on commit 333a43e

Please sign in to comment.