Need help with column printing

Discussion in 'Perl Misc' started by Lynn, Jun 16, 2004.

  1. Lynn

    Lynn Guest

    Hi All,

    I am having problems generating a report (Columns) from data
    that is in the form of associative arrays of associative arrays.

    I have provided an example of what I am trying to do. I the problem
    is I am unable to align the percent column. Any advise as
    how to fix this?

    Thanks

    Lynn

    #!/app/bin/perl
    use warnings;
    use strict;
    use diagnostics;
    use Text::Table;

    my %people = (
    'hr' => {
    'TOTAL' => 15,
    'WIN' => 11,
    'HP' => 4,
    'BLOG' => 2
    },
    'jg' => {
    'TOTAL' => 14,
    'WIN' => 5,
    'SGI' => 9,
    'BLOG' => 4
    },
    'my' => {
    'TOTAL' => 10,
    'WIN' => 9,
    'BLOG' => 1,
    'HP' => 1
    },
    'gh' => {
    'TOTAL' => 13,
    'WIN' => 8,
    'SUN' => 5,
    'BLOG' => 1
    }
    );
    use vars qw(@by_family $TOTAL $BLOG $HP $IBM $SGI $SUN $WIN $OTH);

    my @stuff = qw(TOTAL BLOG HP IBM SGI SUN WIN OTH);

    while ( my ( $item, $record ) = each %people ) {
    $TOTAL += $record->{TOTAL} unless !( defined( $record->{TOTAL} ) );
    $OTH += $record->{OTH} unless !( defined( $record->{OTH} ) );
    $BLOG += $record->{BLOG} unless !( defined( $record->{BLOG} ) );
    $HP += $record->{HP} unless !( defined( $record->{HP} ) );
    $IBM += $record->{IBM} unless !( defined( $record->{IBM} ) );
    $SGI += $record->{SGI} unless !( defined( $record->{SGI} ) );
    $SUN += $record->{SUN} unless !( defined( $record->{SUN} ) );
    $WIN += $record->{WIN} unless !( defined( $record->{WIN} ) );
    }

    push ( @by_family, $TOTAL, $BLOG, $HP, $IBM, $SGI, $SUN, $WIN, $OTH );

    my @percent = ();

    push ( @percent, sprintf( '%2d', ( 100 * $by_family[2] / $by_family[0] ) ) )
    unless !( defined( $by_family[2] ) or '0' );
    push ( @percent, sprintf( '%2d', ( 100 * $by_family[3] / $by_family[0] ) ) )
    unless !( defined( $by_family[3] ) or '0');
    push ( @percent, sprintf( '%2d', ( 100 * $by_family[4] / $by_family[0] ) ) )
    unless !( defined( $by_family[4] ) or '0');
    push ( @percent, sprintf( '%2d', ( 100 * $by_family[5] / $by_family[0] ) ) )
    unless !( defined( $by_family[5] ) or '0');
    push ( @percent, sprintf( '%2d', ( 100 * $by_family[6] / $by_family[0] ) ) )
    unless !( defined( $by_family[6] ) or '0');
    push ( @percent, sprintf( '%2d', ( 100 * $by_family[7] / $by_family[0] ) ) )
    unless !( defined( $by_family[7] ) or '0');

    my $tb = Text::Table->new( \"|", 'Name', \"|", @stuff );
    $tb->add( $_, @{ $people{$_} }{@stuff} ) for sort keys
    %people;
    $tb->add( 'TOTAL:', @by_family );
    $tb->add( 'PERCENT TOTAL:','N/A', 'N/A', @percent );
    print $tb;
    Lynn, Jun 16, 2004
    #1
    1. Advertising

  2. Lynn

    Anno Siegel Guest

    Lynn <> wrote in comp.lang.perl.misc:
    > Hi All,
    >
    > I am having problems generating a report (Columns) from data
    > that is in the form of associative arrays of associative arrays.
    >
    > I have provided an example of what I am trying to do. I the problem
    > is I am unable to align the percent column. Any advise as
    > how to fix this?


    First off, thanks for supplying runnable code that shows the problem.
    That makes my job a lot easier. (This is "my job" because I happen to
    be the author of Text::Table.)

    The alignment is messed up where you build the @percent array. See my
    comment in the code.

    > #!/app/bin/perl
    > use warnings;
    > use strict;
    > use diagnostics;
    > use Text::Table;
    >
    > my %people = (
    > 'hr' => {
    > 'TOTAL' => 15,
    > 'WIN' => 11,
    > 'HP' => 4,
    > 'BLOG' => 2
    > },
    > 'jg' => {
    > 'TOTAL' => 14,
    > 'WIN' => 5,
    > 'SGI' => 9,
    > 'BLOG' => 4
    > },
    > 'my' => {
    > 'TOTAL' => 10,
    > 'WIN' => 9,
    > 'BLOG' => 1,
    > 'HP' => 1
    > },
    > 'gh' => {
    > 'TOTAL' => 13,
    > 'WIN' => 8,
    > 'SUN' => 5,
    > 'BLOG' => 1
    > }
    > );
    > use vars qw(@by_family $TOTAL $BLOG $HP $IBM $SGI $SUN $WIN $OTH);


    Why are you using package variables here? From the posted code, there
    is no reason why these couldn't be lexicals like other variables.

    > my @stuff = qw(TOTAL BLOG HP IBM SGI SUN WIN OTH);


    Putting the keys into a sequence is a good idea, but you're not making
    good use of the list.

    > while ( my ( $item, $record ) = each %people ) {
    > $TOTAL += $record->{TOTAL} unless !( defined( $record->{TOTAL} ) );
    > $OTH += $record->{OTH} unless !( defined( $record->{OTH} ) );
    > $BLOG += $record->{BLOG} unless !( defined( $record->{BLOG} ) );
    > $HP += $record->{HP} unless !( defined( $record->{HP} ) );
    > $IBM += $record->{IBM} unless !( defined( $record->{IBM} ) );
    > $SGI += $record->{SGI} unless !( defined( $record->{SGI} ) );
    > $SUN += $record->{SUN} unless !( defined( $record->{SUN} ) );
    > $WIN += $record->{WIN} unless !( defined( $record->{WIN} ) );
    > }


    If you *want* undef to be treated as zero, that's a good time to say

    no warnings 'uninitialized';

    and let Perl handle it.

    > push ( @by_family, $TOTAL, $BLOG, $HP, $IBM, $SGI, $SUN, $WIN, $OTH );


    Why the push()? @by_family is empty, so an assignment would be clearer.

    @by_family = ( $TOTAL, $BLOG, $HP, $IBM, $SGI, $SUN, $WIN, $OTH );

    > my @percent = ();
    >
    > push ( @percent, sprintf( '%2d', ( 100 * $by_family[2] / $by_family[0] ) ) )
    > unless !( defined( $by_family[2] ) or '0' );


    "unless !" is better written as "if". Also, "or '0'" does nothing to
    the boolean value of the expression. The whole condition could be written

    if defined $by_family[ 2];

    ....but it is wrong anyway. If a column isn't defined, you should push
    a 0, or a "", or a "-", or anything onto the array. Otherwise the *next*
    column will slip into its place. That's what messes up the alignment.

    > push ( @percent, sprintf( '%2d', ( 100 * $by_family[3] / $by_family[0] ) ) )
    > unless !( defined( $by_family[3] ) or '0');
    > push ( @percent, sprintf( '%2d', ( 100 * $by_family[4] / $by_family[0] ) ) )
    > unless !( defined( $by_family[4] ) or '0');
    > push ( @percent, sprintf( '%2d', ( 100 * $by_family[5] / $by_family[0] ) ) )
    > unless !( defined( $by_family[5] ) or '0');
    > push ( @percent, sprintf( '%2d', ( 100 * $by_family[6] / $by_family[0] ) ) )
    > unless !( defined( $by_family[6] ) or '0');
    > push ( @percent, sprintf( '%2d', ( 100 * $by_family[7] / $by_family[0] ) ) )
    > unless !( defined( $by_family[7] ) or '0');


    > my $tb = Text::Table->new( \"|", 'Name', \"|", @stuff );
    > $tb->add( $_, @{ $people{$_} }{@stuff} ) for sort keys
    > %people;
    > $tb->add( 'TOTAL:', @by_family );
    > $tb->add( 'PERCENT TOTAL:','N/A', 'N/A', @percent );
    > print $tb;


    You have made it unnecessary hard to get the alignment right. For the
    "TOTAL" and "PERCENT TOTAL" lines you are using arrays, whiled you use
    a hash for the fundamental data structure.

    Build the "total" and "percent" structures after the model of your basic
    structure, as hashes keyed on @stuff. Then it will be much easier to
    keep things aligned. After the setup of %people, this is how I would
    continue:

    my @stuff = qw(TOTAL BLOG HP IBM SGI SUN WIN OTH);

    my %by_family;
    while ( my ( $item, $record) = each %people ) {
    no warnings 'uninitialized';
    $by_family{ $_} += $record->{ $_} for @stuff;
    }

    my %percent;
    $percent{ $_} = 100 * $by_family{ $_} / $by_family{ TOTAL} for @stuff;
    $_ = sprintf '%.0f', $_ for values %percent;

    my $tb = Text::Table->new( \"|", 'Name', \"|", @stuff );
    $tb->add( $_, @{ $people{$_} }{@stuff} ) for sort keys %people;
    $tb->add( 'TOTAL:', @by_family{ @stuff} );
    $tb->add( 'PERCENT TOTAL:', 'N/A', 'N/A', @percent{ @stuff[ 2 .. $#stuff]});
    print $tb;

    The percent data is calculated for two cases that aren't needed (TOTAL and
    BLOG). It would have been more difficult to exclude them. Instead, we
    exclude them using only @stuff[ 2 .. $#stuff]. Add two 'N/A's, skip
    two items in @stuff... the columns clearly stay aligned.

    Anno
    Anno Siegel, Jun 16, 2004
    #2
    1. Advertising

  3. Lynn

    thundergnat Guest

    Lynn wrote:
    > Hi All,
    >
    > I am having problems generating a report (Columns) from data
    > that is in the form of associative arrays of associative arrays.
    >
    > I have provided an example of what I am trying to do. I the problem
    > is I am unable to align the percent column. Any advise as
    > how to fix this?
    >
    > Thanks
    >
    > Lynn
    >


    The misalignment is due to the different handling of numbers and
    strings. Your counts are all numbers but your percentages are strings.
    If you make your percentages numbers too, they all line up.

    Read the doc for Text::Aligner on which Text::Table is based.

    Your data structures cry out for hashes or arrays too.



    #!/app/bin/perl
    use warnings;
    use strict;
    use diagnostics;
    use Text::Table;

    my %people = (
    'hr' => {
    'TOTAL' => 15,
    'WIN' => 11,
    'HP' => 4,
    'BLOG' => 2
    },
    'jg' => {
    'TOTAL' => 14,
    'WIN' => 5,
    'SGI' => 9,
    'BLOG' => 4
    },
    'my' => {
    'TOTAL' => 10,
    'WIN' => 9,
    'BLOG' => 1,
    'HP' => 1
    },
    'gh' => {
    'TOTAL' => 13,
    'WIN' => 8,
    'SUN' => 5,
    'BLOG' => 1
    }
    );

    my %counts;
    my @percent = ();
    my @totals = ();
    my @stuff = qw(TOTAL BLOG HP IBM SGI SUN WIN OTH);

    while ( my ( $item, $record ) = each %people ) {
    for (@stuff){
    $counts{$_} = 0 unless $counts{$_};
    $counts{$_} += $record->{$_} if $record->{$_}
    }
    }

    for ('HP','IBM','SGI','SUN','WIN','OTH'){
    push ( @percent, int( 100 * $counts{$_} / $counts{TOTAL} ) );
    }

    for (@stuff){
    push ( @totals, $counts{$_} );
    }

    my $tb = Text::Table->new( \"|", 'Name', \"|", @stuff );
    $tb->add( $_, @{ $people{$_} }{@stuff} ) for sort keys
    %people;
    $tb->add( 'TOTAL:', @totals );
    $tb->add( 'PERCENT TOTAL:', 'N/A', 'N/A', @percent );
    print $tb;
    thundergnat, Jun 16, 2004
    #3
  4. Lynn

    Lynn Guest

    Anno Siegel wrote:
    > Lynn <> wrote in comp.lang.perl.misc:
    >> Hi All,
    >>

    >
    > First off, thanks for supplying runnable code that shows the problem.
    > That makes my job a lot easier. (This is "my job" because I happen to
    > be the author of Text::Table.)

    Anything I can do to get an answer I will do. Doesn't evernone do that?
    after all I do want to get a reply to my question :)

    >
    > The alignment is messed up where you build the @percent array. See my
    > comment in the code.
    >

    [snipped]

    >> use vars qw(@by_family $TOTAL $BLOG $HP $IBM $SGI $SUN $WIN $OTH);

    >
    > Why are you using package variables here? From the posted code, there
    > is no reason why these couldn't be lexicals like other variables.


    Because I was getting the "Global symbol "whatever" requires explicit
    package name errors.
    It was the only way I knew how to get rid of the warnings.

    >
    >> my @stuff = qw(TOTAL BLOG HP IBM SGI SUN WIN OTH);

    >
    > Putting the keys into a sequence is a good idea, but you're not making
    > good use of the list.


    Ok
    >

    [snipped]
    >
    > If you *want* undef to be treated as zero, that's a good time to say
    >
    > no warnings 'uninitialized';
    >
    > and let Perl handle it.
    >

    I was unaware of this. When ever I start to develop a script I use the
    use warnings line and leave warnings alone.

    >> push ( @by_family, $TOTAL, $BLOG, $HP, $IBM, $SGI, $SUN, $WIN, $OTH
    >> );

    >
    > Why the push()? @by_family is empty, so an assignment would be
    > clearer.
    >
    > @by_family = ( $TOTAL, $BLOG, $HP, $IBM, $SGI, $SUN, $WIN, $OTH );


    Ok, I understand.

    >
    >> my @percent = ();
    >>
    >> push ( @percent, sprintf( '%2d', ( 100 * $by_family[2] /
    >> $by_family[0] ) ) ) unless !( defined( $by_family[2] ) or '0' );

    >
    > "unless !" is better written as "if". Also, "or '0'" does nothing to
    > the boolean value of the expression. The whole condition could be
    > written
    >
    > if defined $by_family[ 2];


    yes, that would be cleaner

    >
    > ...but it is wrong anyway. If a column isn't defined, you should push
    > a 0, or a "", or a "-", or anything onto the array. Otherwise the
    > *next* column will slip into its place. That's what messes up the
    > alignment.


    Ok, here is where I made my mistake.

    >

    [snipped incorrect code]
    >
    > You have made it unnecessary hard to get the alignment right. For the
    > "TOTAL" and "PERCENT TOTAL" lines you are using arrays, whiled you use
    > a hash for the fundamental data structure.
    >
    > Build the "total" and "percent" structures after the model of your
    > basic structure, as hashes keyed on @stuff. Then it will be much
    > easier to
    > keep things aligned. After the setup of %people, this is how I would
    > continue:
    >

    [snipped]
    > The percent data is calculated for two cases that aren't needed
    > (TOTAL and BLOG). It would have been more difficult to exclude them.
    > Instead, we
    > exclude them using only @stuff[ 2 .. $#stuff]. Add two 'N/A's, skip
    > two items in @stuff... the columns clearly stay aligned.


    yep! This makes sense :)
    I would like to thank you for taking the time to respond.
    It's nice to know that newbies to perl/programming have a place
    to go for help.

    Lynn
    Lynn, Jun 18, 2004
    #4
  5. Lynn

    Anno Siegel Guest

    thundergnat <> wrote in comp.lang.perl.misc:
    > Lynn wrote:
    > > Hi All,
    > >
    > > I am having problems generating a report (Columns) from data
    > > that is in the form of associative arrays of associative arrays.
    > >
    > > I have provided an example of what I am trying to do. I the problem
    > > is I am unable to align the percent column. Any advise as
    > > how to fix this?
    > >
    > > Thanks
    > >
    > > Lynn
    > >

    >
    > The misalignment is due to the different handling of numbers and
    > strings. Your counts are all numbers but your percentages are strings.
    > If you make your percentages numbers too, they all line up.


    That is not the reason. Text::Table (and Text::Aligner) treat all
    data as strings. The problem was that the percentage line didn't
    *have* data for all columns.

    Anno
    Anno Siegel, Jun 18, 2004
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jon B
    Replies:
    7
    Views:
    7,479
    =?Utf-8?B?SmFzb25DaG9p?=
    Jan 30, 2006
  2. JustSomeGuy
    Replies:
    13
    Views:
    502
    msalters
    Dec 9, 2004
  3. iffy agbim
    Replies:
    1
    Views:
    95
    Mark Andrews
    May 24, 2004
  4. iffy agbim
    Replies:
    0
    Views:
    96
    iffy agbim
    May 21, 2004
Loading...

Share This Page