App-RecordStream
view release on metacpan or search on metacpan
lib/App/RecordStream/Operation/toptable.pm view on Meta::CPAN
my ($comparator, $comp_field) = App::RecordStream::Record::get_comparator_and_field("$field=*");
$this->{'SORTS'}->{$field} = $comparator;
}
}
# Prep v fields
if($do_vfields) {
my %vfields;
my %used_first_level_keys;
for my $record (@$records) {
foreach my $spec (@xfields, @yfields, keys %pins) {
my $key_list = $record->get_key_list_for_spec($spec);
if (scalar @$key_list > 0) {
$used_first_level_keys{$key_list->[0]} = 1;
}
}
foreach my $field (keys(%$record)) {
if ( !exists($used_first_level_keys{$field}) &&
!exists($vfields{$field}) ) {
push @vfields, $field;
$vfields{$field} = 1;
}
}
}
# lexically sort if user didn't explicitly specify any vfields
@vfields = sort @vfields;
}
else {
my $vfields_hash = {};
foreach my $record (@$records) {
foreach my $spec ( @{$vgroup->get_keyspecs_for_record($record)} ) {
if ( !$vfields_hash->{$spec} ) {
$vfields_hash->{$spec} = 1;
push @vfields, $spec;
}
}
}
}
# pass 1: build xvals and yvals structures and break records up by vfield
my @r2;
# x_values_tree represent a nested tree of all possible x value tuples
# i.e. {x2 => 4, y => 7, x1 => 1} has an x values tuple of (1, 4) and thus with "touch" that path in the tree
my $x_values_tree = _new_node();
my $y_values_tree = _new_node();
foreach my $record (@$records) {
# make sure records matches appropriate pins
my $kickout = 0;
foreach my $pfield (keys(%pins)) {
if($pfield eq "FIELD") {
next;
}
my $v = '';
if ( $record->has_key_spec($pfield) ) {
$v = ${$record->guess_key_from_spec($pfield)};
}
if($pins{$pfield} ne $v) {
$kickout = 1;
last;
}
}
if($kickout) {
next;
}
for my $vfield (@vfields) {
# nothing to see here
if(!$record->has_key_spec($vfield)) {
next;
}
# if field is pinned, skip other vfields
if(exists($pins{"FIELD"}) && $pins{"FIELD"} ne $vfield) {
next;
}
my @xv;
for my $xfield (@xfields) {
my $v = "";
if($xfield eq "FIELD") {
$v = $vfield;
}
elsif($record->has_key_spec($xfield)) {
$v = ${$record->guess_key_from_spec($xfield)};
}
push @xv, $v;
}
my @yv;
for my $yfield (@yfields) {
my $v = "";
if($yfield eq "FIELD") {
$v = $vfield;
}
elsif($record->has_key_spec($yfield)) {
$v = ${$record->guess_key_from_spec($yfield)};
}
push @yv, $v;
}
my $v = "";
if($record->has_key_spec($vfield)) {
$v = ${$record->guess_key_from_spec($vfield)};
}
_touch_node_recurse($x_values_tree, @xv);
_touch_node_recurse($y_values_tree, @yv);
push @r2, [\@xv, \@yv, $v];
}
}
# Start constructing the ASCII table
# we dump the tree out into all possible x value tuples (saved in
# @x_value_list) and tag each node in the tree with the index in
# @x_values_list so we can look it up later
my @x_values_list;
$this->_dump_node_recurse($x_values_tree, \@x_values_list, [@xfields], []);
my @y_values_list;
$this->_dump_node_recurse($y_values_tree, \@y_values_list, [@yfields], []);
# Collected the data, if we're only outputing records, stop here.
if ( $this->{'OUTPUT_RECORDS'} ) {
$this->output_records(\@xfields, \@yfields, \@r2, \@x_values_list, \@y_values_list);
return;
}
my $width_offset = scalar @yfields;
my $height_offset = scalar @xfields;
if ( $headers ) {
$width_offset += 1;
$height_offset += 1;
}
my $w = $width_offset + scalar(@x_values_list);
my $h = $height_offset + scalar(@y_values_list);
my @table = map { [map { "" } (1..$w)] } (1..$h);
if ( $headers ) {
for(my $i = 0; $i < @xfields; ++$i) {
$table[$i]->[scalar(@yfields)] = $xfields[$i];
}
for(my $i = 0; $i < @yfields; ++$i) {
$table[scalar(@xfields)]->[$i] = $yfields[$i];
}
}
my @last_xv = map { "" } (1..@xfields);
for(my $i = 0; $i < @x_values_list; ++$i) {
my $xv = $x_values_list[$i];
for(my $j = 0; $j < @xfields; ++$j) {
if($last_xv[$j] ne $xv->[$j]) {
$last_xv[$j] = $xv->[$j];
$table[$j]->[$width_offset + $i] = $xv->[$j];
for(my $k = $j + 1; $k < @xfields; ++$k) {
$last_xv[$k] = "";
}
}
( run in 1.085 second using v1.01-cache-2.11-cpan-39bf76dae61 )