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 )