File Coverage

blib/lib/Data/TableAutoSum.pm
Criterion Covered Total %
statement 138 138 100.0
branch 16 16 100.0
condition 12 17 70.5
subroutine 31 31 100.0
pod 15 17 88.2
total 212 219 96.8


line stmt bran cond sub pod time code
1             package Data::TableAutoSum;
2              
3 10     10   158978 use strict;
  10         77  
  10         285  
4 10     10   52 use warnings;
  10         18  
  10         617  
5              
6             require Exporter;
7              
8             our @ISA = qw(Exporter);
9              
10             # I export nothing, so there aren't any @EXPORT* declarations
11              
12             our $VERSION = '0.11';
13              
14 10     10   5472 use Params::Validate qw/:all/;
  10         95791  
  10         1683  
15 10     10   5539 use Regexp::Common;
  10         51271  
  10         50  
16 10     10   1649019 use Set::Scalar;
  10         112107  
  10         516  
17 10     10   83 use List::Util qw/reduce/;
  10         26  
  10         1201  
18 10     10   5377 use Tie::CSV_File;
  10         541646  
  10         862  
19 10     10   5365 use Data::Compare;
  10         146363  
  10         102  
20              
21             sub implies($$) {
22 1510     1510 0 94621 my ($x, $y) = @_;
23 1510   66     13992 return !$x || ($x && $y);
24             }
25              
26             sub is_uniq(@) {
27 381     381 0 619 my %items;
28 381         868 foreach (@_) {
29 25098 100       51993 return 0 if $items{$_}++;
30             }
31 379         2738 return 1;
32             }
33              
34             use constant ROW_COL_TYPE => {
35             type => SCALAR | ARRAYREF,
36             callbacks => {
37             # scalar value
38 375         2240 'integer' => sub { implies !ref($_[0]) => $_[0] =~ $RE{num}{int} },
39 379   100     1912 'greater than 0' => sub { implies !ref($_[0]) => ($_[0] =~ $RE{num}{int}) && (int($_[0]) > 0) },
40              
41             # array ref
42 10     10   40857 'uniq identifiers' => sub { no strict 'refs';
  10         36  
  10         868  
43 381         831 implies ref($_[0]) => is_uniq @{$_[0]} },
  381         1539  
44 10     10   79 'some identifiers' => sub { no strict 'refs';
  10         38  
  10         793  
45 375         830 implies ref($_[0]) => @{$_[0]} }
  375         1391  
46             }
47 10     10   92 };
  10         26  
  10         20059  
48              
49             sub new {
50 219     219 1 470809 my $proto = shift;
51 219         7242 my %arg = validate( @_ => {rows => ROW_COL_TYPE, cols => ROW_COL_TYPE} );
52 184   33     1239 my $class = ref($proto) || $proto;
53 184 100       1868 my @rows = ref($arg{rows}) ? @{$arg{rows}} : (0 .. $arg{rows}-1);
  87         1746  
54 184 100       1644 my @cols = ref($arg{cols}) ? @{$arg{cols}} : (0 .. $arg{cols}-1);
  87         1753  
55 184         399 my %data;
56 184         430 foreach my $row (@rows) {
57 26847         35481 foreach my $col (@cols) {
58 88042         159745 $data{$row}->{$col} = 0;
59             }
60             }
61 184         1335 my $self = {
62             rows => \@rows,
63             rowset => Set::Scalar->new(@rows),
64             cols => \@cols,
65             colset => Set::Scalar->new(@cols),
66             data => \%data
67             };
68 184         308983 bless $self, $class;
69             }
70              
71             sub rows {
72 17702     17702 1 56973 my $self = shift;
73 17702         21397 return @{$self->{rows}};
  17702         63607  
74             }
75              
76             sub cols {
77 47963     47963 1 696588 my $self = shift;
78 47963         57688 return @{$self->{cols}};
  47963         140739  
79             }
80              
81             sub data : lvalue {
82 413919     413919 1 4668907 my $self = shift;
83             my ($row, $col, $value) = validate_pos( @_,
84             {type => SCALAR,
85 413919     413919   1057262 callbacks => {'is a row' => sub {$self->{rowset}->contains(shift())}}
86             },
87             {type => SCALAR,
88 413863     413863   5374831 callbacks => {'is a col' => sub {$self->{colset}->contains(shift())}}
89             },
90 413919         4160483 0
91             );
92 413807 100       5396815 $self->{data}->{$row}->{$col} = $value if defined $value;
93 413807         1379348 $self->{data}->{$row}->{$col};
94             }
95              
96             sub as_string {
97 77     77 1 19723 my $self = shift;
98 77         258 my $output = join "\t", "", $self->cols, "Sum\n";
99 77         582 foreach my $row ($self->rows) {
100 10331         16426 $output .= $row . "\t";
101 10331         16667 $output .= join "\t", map {$self->data($row,$_)} ($self->cols);
  32631         51750  
102 10331         22155 $output .= "\t" . $self->rowresult($row) . "\n";
103             }
104 77         1583 $output .= join "\t", "Sum", map {$self->colresult($_)} $self->cols;
  10599         17013  
105 77         1563 $output .= "\t" . $self->totalresult . "\n";
106 77         3579 return $output;
107             }
108              
109             sub store {
110 29     29 1 740 local $| = 1;
111 29         76 my ($self, $filename) = @_;
112 29 100 50     7193 defined($filename) && (open FILE, ">$filename") or die "Can't open filename '" . ($filename // '') . "' to store the table";
      66        
113 28         221 print FILE $self->as_string;
114 28         4401 close FILE;
115 28         307 return $self;
116             }
117              
118             sub read {
119 15     15 1 442 my ($class, $filename) = @_;
120 15 100       70 defined($filename) or die "Can't open undefined fileman in Data::TableAutoSum->read";
121 14         125 tie my @data, 'Tie::CSV_File', $filename, sep_char => "\t",
122             quote_char => undef,
123             escape_char => undef;
124              
125            
126 14         6657 my @header = @{ $data[0] };
  14         85  
127 14         12946 my @col = @header[1 .. $#header-1];
128 14         100 my @row = map {$data[$_]->[0]} (1 .. $#data-1);
  2064         381898  
129 14         3734 my $table = $class->new(rows => \@row, cols => \@col);
130            
131 14         51 foreach my $i (0 .. $#row) {
132 2064         4608 foreach my $j (0 .. $#col) {
133 6518         24813 $table->data($row[$i],$col[$j]) = $data[$i+1][$j+1];
134             }
135             }
136            
137 14         101 untie @data;
138 14         3089 return $table;
139             }
140              
141             sub change {
142 51     51 1 3635 my ($self, $sub) = @_;
143 51         139 foreach my $row ($self->rows) {
144 7226         14809 foreach my $col ($self->cols) {
145 22815         43605 local $_ = $self->data($row,$col);
146 22815         51957 &$sub;
147 22813         138639 $self->data($row,$col) = $_;
148             }
149             }
150             }
151              
152             sub merge {
153 14     14 1 150 my ($class, $sub, $table1, $table2) = @_;
154 14         39 my @row = $table1->rows;
155 14         47 my @col = $table1->cols;
156 14         72 my $merged = $class->new(rows => \@row, cols => \@col);
157 14         41 foreach my $i (@row) {
158 2064         3457 foreach my $j (@col) {
159 6518         12462 $merged->data($i,$j) = $sub->($table1->data($i,$j), $table2->data($i,$j));
160             }
161             }
162 14         289 return $merged;
163             }
164              
165             sub _calc_data {
166 29405     29405   42006 my $result = $_[0];
167 29405         81103 $result += $_[$_] for (1 .. $#_);
168 29405         76626 return $result;
169             }
170              
171             sub rowresult {
172 14459     14459 1 39688 my ($self, $row) = @_;
173 14459         24362 return _calc_data( map {$self->data($row,$_)} $self->cols );
  45667         78968  
174             }
175              
176             sub colresult {
177 14827     14827 1 40499 my ($self, $col) = @_;
178 14827         22538 return _calc_data( map {$self->data($_,$col)} $self->rows );
  45667         74317  
179             }
180              
181             sub totalresult {
182 119     119 1 401 my $self = shift;
183 119         196 return _calc_data( map {values %$_} values %{$self->{data}} );
  16523         37715  
  119         1092  
184             }
185              
186             sub contains_row {
187 2089     2089 1 25068 my ($self, $row) = @_;
188 2089         3629 $self->{rowset}->contains($row);
189             }
190              
191             sub contains_col {
192 2089     2089 1 26771 my ($self, $col) = @_;
193 2089         3585 $self->{colset}->contains($col);
194             }
195              
196             sub is_equal {
197 155     155 1 1048 my ($self, $other) = @_;
198 155 100 100     497 Compare( [$self->rows], [$other->rows] ) &&
199             Compare( [$self->cols], [$other->cols] ) or return 0;
200 121         557092 foreach my $row ($self->rows) {
201 8118         16165 foreach my $col ($self->cols) {
202 64969 100       119909 $self->data($row,$col) == $other->data($row,$col) or return 0;
203             }
204             }
205 64         1157 1;
206             }
207              
208             1;
209             __END__