Formatting a single line without effecting the entire table with the Text::Table module

M

Mothra

Hi All,

I have a data structure (see script) that I need to printout. I almost have
what I
need but I cannot figure out how to format just one line (center it )
sample output is:
kreimer
EMMI CHRISTIAN 0 0 17
TRACEY LUSBY 0 4 0
TONA MCGEAN 0 7 0
Total: 0 11 17

What I would like is the word "kreimer" centered and the other names
remain left justified. What am I doing wrong?

Thanks

Lynn




----------script--------------
use strict;
use warnings;
use Text::Table;
use diagnostics;
use Data::Dumper;

my $people = {
'kreimer' => {
'TRACEY LUSBY' => [ 0, 4, 0 ],
'EMMI CHRISTIAN' => [ 0, 0, 17 ],
'TONA MCGEAN' => [ 0, 7, 0 ],

},
'OTHER' => {
'STEPHEN ESPESET' => [ undef, 4, 27 ],
'PATRICK HOONHOU' => [ 1, 3, 4 ],
'WOODROW WILSON' => [ undef, 2, 1 ],

},
'lusby' => {
'KEVIN FLUETSCH' => [ 0, 0, 22 ],
'DOUG BOSWORTH' => [ 0, 1, 6 ],
'DOUG PENICK' => [ 1, 2, 22 ],

},

};

foreach my $tmp_people( values %{ $people->{'OTHER'} } ) {

if ( !defined( $tmp_people->[0] ) ) { $tmp_people->[0] = 0 }
if ( !defined( $tmp_people->[1] ) ) { $tmp_people->[1] = 0 }
if ( !defined( $tmp_people->[2] ) ) { $tmp_people->[2] = 0 }
}

my %manager_total = ();
foreach my $managers( keys %{$people} ) {
foreach my $person( keys %{ $people->{$managers} } ) {
$manager_total{$managers}[0] += $people->{$managers}->{$person}[0];
$manager_total{$managers}[1] += $people->{$managers}->{$person}[1];
$manager_total{$managers}[2] += $people->{$managers}->{$person}[2];
}
}

my $tb = Text::Table->new(

{
title => 'Name',
align => 'left',
align_title => 'center'
},

{
title => 'Last Month',
align => 'center',
align_title => 'center'
},

{
title => 'YTD',
align => 'center',
align_title => 'center'
},

{
title => 'Rolling 12 months',
align => 'center',
align_title => 'center'
},

);

#print Dumper (\%manager_total);

foreach my $managers( keys %{$people} ) {
$tb->add($managers);

foreach my $person( sort Mysort keys %{ $people->{$managers} } ) {

$tb->add( "$person", @{ $people->{$managers}->{$person} } );


}
$tb->add( "Total: ", @{ $manager_total{$managers} } );
$tb->add();
}

print $tb;

sub Mysort {

my ( undef, $first ) = split ( / /, $a );
my ( undef, $last ) = split ( / /, $b );
$first cmp $last;

}
 
T

Tad McClellan

Mothra said:
if ( !defined( $tmp_people->[0] ) ) { $tmp_people->[0] = 0 }


Excessive punctuation is bad for your (mental) health:

$tmp_people->[0] = 0 unless defined $tmp_people->[0];

if ( !defined( $tmp_people->[1] ) ) { $tmp_people->[1] = 0 }
if ( !defined( $tmp_people->[2] ) ) { $tmp_people->[2] = 0 }


Gak! There are only 2 characters that are different between all
three of those lines, that indicates that it can be written better.

foreach ( 0 .. 2 ) {
$tmp_people->[$_] = 0 unless defined $tmp_people->[$_];
}

but that won't scale when you add a 4th tmp_people, so maybe:

foreach ( 0 .. $#{ $tmp_people } ) {
$tmp_people->[$_] = 0 unless defined $tmp_people->[$_];
}

but since foreach uses an alias, we can make it even simpler
by not doing the indexing ourselves, let perl do the indexing:

$_ = defined() ? $_ : 0 for @$tmp_people; # untested

or

for ( @$tmp_people ) {
$_ = defined() ? $_ : 0;
}

or

for ( @$tmp_people ) { # this would be my choice of replacement code
$_ = 0 unless defined;
}

$manager_total{$managers}[0] += $people->{$managers}->{$person}[0];
$manager_total{$managers}[1] += $people->{$managers}->{$person}[1];
$manager_total{$managers}[2] += $people->{$managers}->{$person}[2];


When you see repeated patterns in code like that, it usually
means you can easily come up with a better design that doesn't
repeat so much.
 
F

Fabian Pilkowski

* Mothra said:
I have a data structure (see script) that I need to printout. I almost
have what I need but I cannot figure out how to format just one line
(center it ) sample output is:

kreimer
EMMI CHRISTIAN 0 0 17
TRACEY LUSBY 0 4 0
TONA MCGEAN 0 7 0
Total: 0 11 17

What I would like is the word "kreimer" centered and the other names
remain left justified. What am I doing wrong?

After reading this module's documentation i'm thinking there's no way to
format a few lines differently than others. Consider to use another
format to indicate such "captions". Perhaps something like indentions:

kreimer
EMMI CHRISTIAN 0 0 17
TRACEY LUSBY 0 4 0
TONA MCGEAN 0 7 0
Total: 0 11 17
----------script--------------

foreach my $tmp_people( values %{ $people->{'OTHER'} } ) {
if ( !defined( $tmp_people->[0] ) ) { $tmp_people->[0] = 0 }
if ( !defined( $tmp_people->[1] ) ) { $tmp_people->[1] = 0 }
if ( !defined( $tmp_people->[2] ) ) { $tmp_people->[2] = 0 }
}

Since youre values are always numbers, this loop can be reduced to:

for ( values %{$people->{OTHER}} ) {
$_ ||= 0 for @$_;
}

For testing for definedness, I've heard about an »//=« operator that
will be introduced in Perl6, but here this should be sufficient.
my %manager_total = ();
foreach my $managers( keys %{$people} ) {
foreach my $person( keys %{ $people->{$managers} } ) {
$manager_total{$managers}[0] += $people->{$managers}->{$person}[0];
$manager_total{$managers}[1] += $people->{$managers}->{$person}[1];
$manager_total{$managers}[2] += $people->{$managers}->{$person}[2];
}
}

For me, your style isn't as good as it could be. Try out:

my %manager_total = ();
for my $m ( keys %{$people} ) {
for my $p ( values %{$people->{$m}} ) {
for my $i ( 0 .. $#{$p} ) {
$manager_total{ $m }[ $i ] += $p->[$i];
}
}
}

[...]
foreach my $managers( keys %{$people} ) {
$tb->add($managers);

foreach my $person( sort Mysort keys %{ $people->{$managers} } ) {

$tb->add( "$person", @{ $people->{$managers}->{$person} } );
^^^

Adding here two blanks will indenting these lines (see above).
}
$tb->add( "Total: ", @{ $manager_total{$managers} } );
$tb->add();

On my system, adding *nothing* will produce the warning "Use of
uninitialized value in range (or flop) at Text/Table.pm line 380".
Perhaps you want to add a blank to generate a blank line

$tb->add( " " );
}

print $tb;

sub Mysort {

my ( undef, $first ) = split ( / /, $a );
my ( undef, $last ) = split ( / /, $b );
$first cmp $last;

}

I'd write this as

sub Mysort {
(split / /, $a)[1] cmp (split / /, $b)[1]
}

but I don't think every proposal above results in better readability.
This might depending on one's individual flair.

regards,
fabian
 
A

Anno Siegel

Mothra said:
Hi All,

I have a data structure (see script) that I need to printout. I almost have
what I
need but I cannot figure out how to format just one line (center it )
sample output is:
kreimer
EMMI CHRISTIAN 0 0 17
TRACEY LUSBY 0 4 0
TONA MCGEAN 0 7 0
Total: 0 11 17

What I would like is the word "kreimer" centered and the other names
remain left justified. What am I doing wrong?

[code appreciated, but snipped]

Text::Table currently only supports one alignment specification for
each column, so there is no way you can require central alignment for
some lines and not for others. You are not the first one who wants
to do that, so a future revision may support it, but that's not coming
soon.

For the moment, if you can do with a fixed amount of indentation instead
of true center alignment, just prefix the (manager-) names with a number
of blanks when you add them (see below).

foreach my $managers( keys %{$people} ) {
$tb->add( " $managers");

Oh, and to add a blank line to the table, add a single empty string,
not an empty list:

$tb->add( '');

If you must have center alignment, you can probably use the ->colrange
method to print the centered names manually. That should be doable,
though I'm not quite sure how I would do it. Ask again if you need it.

Anno
 
A

Anno Siegel

[Text::Table]
On my system, adding *nothing* will produce the warning "Use of
uninitialized value in range (or flop) at Text/Table.pm line 380".

Oh... that's a bug. The next release will silently ignore empty lines.
I can't believe I don't have a test for this. Thanks for the input.
Perhaps you want to add a blank to generate a blank line

$tb->add( " " );

An empty string will do. Even "undef" is accepted (though not recommended)
for the purpose.

Anno (author of Text::Table)
 
L

lynn

Hi Tad,

Tad McClellan said:
Excessive punctuation is bad for your (mental) health: (snipped)

Gak! There are only 2 characters that are different between all
three of those lines, that indicates that it can be written better.
(more snippage)
but since foreach uses an alias, we can make it even simpler
by not doing the indexing ourselves, let perl do the indexing:

$_ = defined() ? $_ : 0 for @$tmp_people; # untested

or

for ( @$tmp_people ) {
$_ = defined() ? $_ : 0;
} (even more snippage)
When you see repeated patterns in code like that, it usually
means you can easily come up with a better design that doesn't
repeat so much.
Thanks for your reply, That is great!! I like what you did to the code :)
BTY, Where is the ? and : documented in perldoc? I can't find them
(this is the reason I use diagnostics in my programs, it tells me what
to type into perldoc)
I tried:
perldoc -f :
No documentation for perl function `:' found
perldoc -f ?
No documentation for perl function `?' found
perldoc ?
No documentation found for "?".
perldoc :
No documentation found for ":".


Thanks

Lynn
 
P

Paul Lalli

lynn said:
BTY, Where is the ? and : documented in perldoc? I can't find them
(this is the reason I use diagnostics in my programs, it tells me what
to type into perldoc)
I tried:
perldoc -f :
No documentation for perl function `:' found
perldoc -f ?
No documentation for perl function `?' found

perldoc -f is for function names. Neither ? nor : are functions
perldoc ?
No documentation found for "?".
perldoc :
No documentation found for ":".

perldoc <topic> for both the predefined documentation that comes with
perl (see perldoc perltoc for a full list) as well as modules. ? and :
are neither.

Indeed, in the code your referenced, ? and : are not separate entities,
but are instead a single operator, ?:

This operator, like all operators, is documented in perldoc perlop. It
is referred to therein as the "conditional operator", but you may also
hear the term "trinary operator" (simply because it takes three
arguments.

Hope this helps,
Paul Lalli
 
L

lynn

Hi Fabian,

(snipped)
After reading this module's documentation i'm thinking there's no way to
format a few lines differently than others. Consider to use another
format to indicate such "captions". Perhaps something like indentions:

After reading the other replys this is what I will do
----------script--------------
(snipped)

Since youre values are always numbers, this loop can be reduced to:
for ( values %{$people->{OTHER}} ) {
$_ ||= 0 for @$_;
}
That looks good :)
For testing for definedness, I've heard about an »//=« operator that
will be introduced in Perl6, but here this should be sufficient.
(more snippage)
For me, your style isn't as good as it could be. Try out:
my %manager_total = ();
for my $m ( keys %{$people} ) {
for my $p ( values %{$people->{$m}} ) {
for my $i ( 0 .. $#{$p} ) {
$manager_total{ $m }[ $i ] += $p->[$i];
}
}
}
Much better :)
foreach my $managers( keys %{$people} ) {
$tb->add($managers);

foreach my $person( sort Mysort keys %{ $people->{$managers} } ) {

$tb->add( "$person", @{ $people->{$managers}->{$person} } ); ^^^

On my system, adding *nothing* will produce the warning "Use of
uninitialized value in range (or flop) at Text/Table.pm line 380".
Perhaps you want to add a blank to generate a blank line

I did not get any warnings, I am running perl 5.8.3 on windows.
(I did use warnings!! )
print $tb;

sub Mysort {

my ( undef, $first ) = split ( / /, $a );
my ( undef, $last ) = split ( / /, $b );
$first cmp $last;

}
I'd write this as
sub Mysort {
(split / /, $a)[1] cmp (split / /, $b)[1]
}

Looks good, Thanks for your response :)


Lynn
 
L

lynn

Hi Anno
(snipped)
[code appreciated, but snipped]

Text::Table currently only supports one alignment specification for
each column, so there is no way you can require central alignment for
some lines and not for others. You are not the first one who wants
to do that, so a future revision may support it, but that's not coming
soon.

First off I would like to say I really like your module :) It makes
creating
reports much easier.
For the moment, if you can do with a fixed amount of indentation instead
of true center alignment, just prefix the (manager-) names with a number
of blanks when you add them (see below).

foreach my $managers( keys %{$people} ) {
$tb->add( " $managers");

This is what I have decided to do, Thanks!!
Oh, and to add a blank line to the table, add a single empty string,
not an empty list:

$tb->add( '');
Yes, I saw the reply from Fabian however, I did not receive the warnings
he was refering to (odd). Thanks for all of your help!!

Lynn
 
L

lynn

Hi Paul,

Paul Lalli said:
(snipped)

perldoc <topic> for both the predefined documentation that comes with
perl (see perldoc perltoc for a full list) as well as modules. ? and :
are neither.

Great ! now I can see the full list :)
Indeed, in the code your referenced, ? and : are not separate entities,
but are instead a single operator, ?:

This operator, like all operators, is documented in perldoc perlop. It
is referred to therein as the "conditional operator", but you may also
hear the term "trinary operator" (simply because it takes three
arguments.

Hope this helps,
Yes!! indeed this does help. Perl is a great programming language
however, finding things in the documentation has caused me so much
grief. I don't blame the doc though more on my ability to use them (perldoc)

Once again, Thanks for all of your help :)

Lynn
 
A

Anno Siegel

[Text::Table]
Yes, I saw the reply from Fabian however, I did not receive the warnings
he was refering to (odd). Thanks for all of your help!!

The bug is only in 1.107. You probably have an earlier version.

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top