Data-TableAutoSum
view release on metacpan or search on metacpan
TableAutoSum.pm view on Meta::CPAN
package Data::TableAutoSum;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
# I export nothing, so there aren't any @EXPORT* declarations
our $VERSION = '0.11';
use Params::Validate qw/:all/;
use Regexp::Common;
use Set::Scalar;
use List::Util qw/reduce/;
use Tie::CSV_File;
use Data::Compare;
sub implies($$) {
my ($x, $y) = @_;
return !$x || ($x && $y);
}
sub is_uniq(@) {
my %items;
foreach (@_) {
return 0 if $items{$_}++;
}
return 1;
}
use constant ROW_COL_TYPE => {
type => SCALAR | ARRAYREF,
callbacks => {
# scalar value
'integer' => sub { implies !ref($_[0]) => $_[0] =~ $RE{num}{int} },
'greater than 0' => sub { implies !ref($_[0]) => ($_[0] =~ $RE{num}{int}) && (int($_[0]) > 0) },
# array ref
'uniq identifiers' => sub { no strict 'refs';
implies ref($_[0]) => is_uniq @{$_[0]} },
'some identifiers' => sub { no strict 'refs';
implies ref($_[0]) => @{$_[0]} }
}
};
sub new {
my $proto = shift;
my %arg = validate( @_ => {rows => ROW_COL_TYPE, cols => ROW_COL_TYPE} );
my $class = ref($proto) || $proto;
my @rows = ref($arg{rows}) ? @{$arg{rows}} : (0 .. $arg{rows}-1);
my @cols = ref($arg{cols}) ? @{$arg{cols}} : (0 .. $arg{cols}-1);
my %data;
foreach my $row (@rows) {
foreach my $col (@cols) {
$data{$row}->{$col} = 0;
}
}
my $self = {
rows => \@rows,
rowset => Set::Scalar->new(@rows),
cols => \@cols,
colset => Set::Scalar->new(@cols),
data => \%data
};
bless $self, $class;
}
sub rows {
my $self = shift;
return @{$self->{rows}};
}
sub cols {
my $self = shift;
return @{$self->{cols}};
}
sub data : lvalue {
my $self = shift;
my ($row, $col, $value) = validate_pos( @_,
{type => SCALAR,
callbacks => {'is a row' => sub {$self->{rowset}->contains(shift())}}
},
{type => SCALAR,
callbacks => {'is a col' => sub {$self->{colset}->contains(shift())}}
},
0
);
$self->{data}->{$row}->{$col} = $value if defined $value;
$self->{data}->{$row}->{$col};
}
sub as_string {
my $self = shift;
my $output = join "\t", "", $self->cols, "Sum\n";
foreach my $row ($self->rows) {
$output .= $row . "\t";
$output .= join "\t", map {$self->data($row,$_)} ($self->cols);
$output .= "\t" . $self->rowresult($row) . "\n";
}
$output .= join "\t", "Sum", map {$self->colresult($_)} $self->cols;
$output .= "\t" . $self->totalresult . "\n";
return $output;
}
sub store {
local $| = 1;
my ($self, $filename) = @_;
defined($filename) && (open FILE, ">$filename") or die "Can't open filename '" . ($filename // '') . "' to store the table";
print FILE $self->as_string;
close FILE;
return $self;
}
sub read {
my ($class, $filename) = @_;
defined($filename) or die "Can't open undefined fileman in Data::TableAutoSum->read";
tie my @data, 'Tie::CSV_File', $filename, sep_char => "\t",
quote_char => undef,
escape_char => undef;
my @header = @{ $data[0] };
my @col = @header[1 .. $#header-1];
my @row = map {$data[$_]->[0]} (1 .. $#data-1);
my $table = $class->new(rows => \@row, cols => \@col);
foreach my $i (0 .. $#row) {
foreach my $j (0 .. $#col) {
$table->data($row[$i],$col[$j]) = $data[$i+1][$j+1];
}
}
untie @data;
return $table;
}
sub change {
my ($self, $sub) = @_;
foreach my $row ($self->rows) {
foreach my $col ($self->cols) {
local $_ = $self->data($row,$col);
&$sub;
$self->data($row,$col) = $_;
}
( run in 0.987 second using v1.01-cache-2.11-cpan-39bf76dae61 )