qball has asked for the wisdom of the Perl Monks concerning the following question:

What's a good way to print alternating row colors in a while loop?
while( @data = $query->fetchrow ) { print REPORT_FILE qq( <tr> <td width=400 align=left color=$rowcolor>$data[0]</td> <td width=50 align=left>$data[1]</td> <td width=50 align=left>$data[2]</td> <td width=50 align=left>$data[3]</td> <td width=50 align=left>$data[4]</td> </tr> ) }
I could cludge something together, but I'd rather do it right from the beginning. Thank you.

qball~"I have node idea?!"

Replies are listed 'Best First'.
Re: alternating row colors
by Masem (Monsignor) on May 18, 2001 at 00:31 UTC
    my @colors=( '#FFFFFF', '#DDDDFF' ); my $row = 0; while ( @data = ... ) { my $rowcolor = $colors[ $row++ % 2 ]; print ... }

    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      Good code but you could make it a little bit more open by removing the 2 and replace it with $#colors
      my @colors=( '#FFFFFF', '#DDDDFF' ); my $row = 0; while ( @data = ... ) { my $rowcolor = $colors[ $row++ % ($#colors + 1) ]; print ... }
      Then if you would like to add more colors to rotation you do not need to change anything besides adding the color.

      --BigJoe

      Learn patience, you must.
      Young PerlMonk, craves Not these things.
      Use the source Luke.

        Personally, I'd find ...

            $colors[ $row++ % @colors ]

        ... easier on the eyes, but aesthetics are always subjective. :)

            --k.


        Out of all the code written .. this may be the best simple version.
Re (tilly) 1: alternating row colors
by tilly (Archbishop) on May 18, 2001 at 01:29 UTC
    I like to rotate colors like this:
    push @colors, shift @colors;
    Then the current color is always the first element.

      That one is interesting to watch behind the scenes. You start out with a simple array. Each time you rotate it either

      1) There is some spare room in the space allocated for the array and an item is moved into that space and Perl notes that the real array is really further down in that block of space than it used to be

      or

      2) There isn't any space left in which case a larger space is allocated for the array and the array is placed at the front of that new bigger space.

      So the "offset" to finding the first entry will progress something like (from a real run using Devel::Peek):
      0, 1, 0, 1, 2, ..., 8, 0, 1, 2, ..., 25, 0, ...

      while the space for the array slowly grows without bound (it appears).

      Update: Sounds like I stopped experimenting just a bit too soon. Good thing I covered my butt with that "it appears" on the end. (:

              - tye (but my friends call me "Tye")
        On both 5.005 and 5.6 (Linux) I do not see behaviour like that. Here is my code:
        use Devel::Peek; my @a = 1..2; for (my $i = 0; 1; $i++) { print "Stats for $i\n"; Dump(\@a); <STDIN>; push @a, shift @a; }
        The line to look for is "ARRAY =..." When there is an offset it will say "(offset=2)". I find it goes 0, 1, 2, then back to 0, then up to 10, back to zero, up to 10, and so on. Now if I try it with an array of 3 elements it goes up to 1, 0-9, 0-25, 0-25, 0-25... With 4 the same thing only it goes up to 24. Etc.

        If anyone wants to play with this, here is an easy script to hack around with. Play with the array size and run it as many times as you want...

        use strict; use IPC::Open3; my $array_size = shift || 2; my $code = <<'CODE'; use Devel::Peek; my @a = 1..COUNT; for (my $i = 0; 1; $i++) { print STDERR "Iteration=$i\n"; Dump(\@a); print STDERR "\nITERATE\n"; push @a, shift @a; } CODE $code =~ s/COUNT/$array_size/; open3(\*PIPE, ">&STDOUT", \*OUTPUT, "perl") or die "Cannot run perl: $ +!"; $/ = "ITERATE"; print PIPE $code; close PIPE; while (<OUTPUT>) { my $i; if (/Iteration=(\d+)/) { $i = $1; } else { die "Cannot find the iteration in\n$_"; } my $off = /offset=(\d+)/ ? $1 : 0; printf "Iteration%8d: Offset%6d\n", $i, $off; }
(Ovid) Re: alternating row colors
by Ovid (Cardinal) on May 18, 2001 at 01:39 UTC

    This is something that I like to use a closure for. The following will print a table with all of the URL and HTML codes, along with the symbol each stands for and the extended ASCII value. The $toggle variable controls the background color used for each row of the table.

    use CGI; use HTML::Entities; use URI::Escape; my $q = CGI->new; my $data; my $toggle = initToggle( 1 ); for my $ASCII ( 0 .. 255 ) { # The following is a hexadecimal list of characters that must be e +ncoded # in query strings my $cgi_chars = '\x00-\x29\x2b\x2c\x2f\x3a-\x40\x5b-\x5e\x60\x7b- +\xff'; # The following is a hexadecimal list of characters that often hav +e their # HTML character code equivalent used instead of the characters th +emselves my $html_chars = '\x00-\x2f\x3a-\x40\x5b-\x5e\x60\x7b-\xff'; my $char = chr( $ASCII ); # The actua +l character my $symbol = encode_entities( $char, $html_chars ); # HTML code + for Web page my $escaped = uri_escape( $char, $cgi_chars ); # CGI repre +sentation my $html_code = encode_entities( $symbol ); # Re-encode + HTML code so that it # disp +lays properly if ( $escaped eq '%20' ) { $escaped .= ' or +' }; # a space m +ay be %20 or a + my @bgcolor = ( '#FFFFFF', '#CCCCCC' ); $data .= $q->Tr( { -align => 'right', -bgcolor => $bgcolor[ &$toggle ] }, $q->td( [ $ASCII < 33 ? '&nbsp;' : $symbol, $ASCI +I, $escaped, $html_code ] ) ); } print $q->header, $q->start_html, $q->table( $data ), $q->end_html; sub initToggle { my $limit = shift; my $count = 0; my $bit = 0; unless ( $limit > 0 ) { die "initToggle() requires a positive argu +ment\n" }; return sub { $bit ^= 1 if $count++ % $limit == 0; } }

    In fact, if you wanted to, you could easily update the closure to return the color values themselves instead of the array index.

    Yeah, I'm sure some of you recognize that table :)

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      The $bit variable in initToggle is superfluous:
      sub makeToggle { my $limit = shift; my $count = 0; die "makeToggle() requires a positive argument\n" unless $limit > 0; return sub { !($count++ % $limit); } }
         MeowChow                                   
                     s aamecha.s a..a\u$&owag.print
Re: alternating row colors
by Russ (Deacon) on May 18, 2001 at 01:02 UTC
    I like to use something like this:
    my @BGColors = ('#dddddd', '#ffffff'); for (1..10){ # As you loop through a database query result or somethin +g print qq{ <tr bgcolor="$BGColors[$flip = !$flip]"> <td>$_</td> </tr> }; }
    Also, see Flipper for more discussion about this kind of thing.

    Russ
    Brainbench 'Most Valuable Professional' for Perl

Re: alternating row colors
by merlyn (Sage) on May 18, 2001 at 01:35 UTC
(tye)Re: alternating row colors
by tye (Sage) on May 18, 2001 at 01:41 UTC
    sub genRotar { my( $pos, @vals )= ( 0, @_ ); return sub { $pos= 0 if @vals <= $pos; $vals[$pos++] } } sub Rotar::TIESCALAR { shift; return bless genRotar( @_ ), 'Rotar'; } sub Rotar::FETCH { $_[0]->(); } my $rowcolor; tie $rowcolor, 'Rotar', '#FF0000', '#00FF00', '#0000FF';
    Then just append the code you already have above.

            - tye (but my friends call me "Tye")
Re: alternating row colors
by traveler (Parson) on May 18, 2001 at 01:51 UTC
    since qball's question was about alternating colors, how about this:
    my $i = 0; while( @data = $query->fetchrow ) { $rowcolor = $i++%2 ? $color1:$color2; print REPORT_FILE qq( <tr> <td width=400 align=left, color=$rowcolor>$data[0]</td> <td width=50 align=left>$data[1]</td> <td width=50 align=left>$data[2]</td> <td width=50 align=left>$data[3]</td> <td width=50 align=left>$data[4]</td> </tr> ) }
    or remove the $i altogether and do
    while( @data = $query->fetchrow ) { $rowcolor = $rowcolor == $color2 ? $color1:$color2; print REPORT_FILE qq(
    --traveler <bold>update:</bold> Of course, this does have elements of others' posts (e.g Masem and Russ). It's just a different way to do it.
      I took traveler's exact code and plopped it in a php script I'm writing. It worked perfectly.

      Excellent!

      qball~"I have node idea?!"
Re: alternating row colors
by buckaduck (Chaplain) on May 18, 2001 at 02:42 UTC
    use Data::Table; use strict; my @headers = qw(Col1 Col2 Col3 Col4 Col5); my @data; while (my @row = $query->fetchrow) { push @data, \@row; } my $table = Data::Table->new(\@data, \@headers, 0); print $table->html;
    But in your case, you might want to create the table from your SQL query directly:
    use Data::Table; use strict; my $table = Data::Table::fromSQL($dbh, "select * from mytable where blah = ? +", ["blah"]); print $table->html;
    The HTML table has alternate-colored rows by default. You can override the colors by specifying parameters to the html() method call. There are also ways to format the rows and columns, if you need to. Check out Data::Table.

    buckaduck

(dkubb) Re: (2) alternating row colors
by dkubb (Deacon) on May 18, 2001 at 09:04 UTC

    IMHO, I'm not a big fan of having my perl code mixed with HTML code. When I really began seperating my logic (perl code) from my presentation code (HTML and the template language), my web-based apps became infinitely simpler to maintain, and develop.

    To get this seperation I use HTML::Template. With this module, you can alternate the colors for each row of data, all from within the template.

    Here's a simple example that will alternate between two colors inside a template:

    <!-- TMPL_LOOP NAME="rows" --> <!-- TMPL_IF NAME="__ODD__" --> #FFFFFF <!-- TMPL_ELSE --> #DDDDFF <!-- /TMPL_IF --> <!-- /TMPL_LOOP -->

    (Of course, in the a real-world this would contain more HTML code, but I didn't want to clutter the example)

    Then I'd use the following code to load the template, loop through the rows, print the results, and alternate the colors on each iteration:

    my $template = HTML::Template->new( filename => 'template_name.tmpl', loop_context_vars => 1, ); $template->param(rows => $dbh->selectall_hashref($statement)); print $cgi->header, $template->output;

    Now, you could do this inside perl code, but I really believe that when you begin to include things other than perl code inside your perl scripts, they become less maintainable, and less elegant.

Re: alternating row colors
by princepawn (Parson) on May 18, 2001 at 02:51 UTC
Re: alternating row colors
by Gloom (Monk) on May 18, 2001 at 14:21 UTC
    Something like :
    $color = ( $bool = ! $bool ) ? "#AAAAAA" : "#DDDDDD";
    Only single line ... and it's maybe somewhat efficient =) ( no matter here )
    _____________________
    Hope this helps
Re: alternating row colors
by Anonymous Monk on May 19, 2001 at 01:23 UTC
    I always use de modulus "%" operator against the number two for an increasing list when I want something to be alternating. Remember that the tag for the background is "bgcolor=" and that any text for the color has to have quotes around it. Merry Christmas, get it? *grin*
    my $color_num = 1; while( @data = $query->fetchrow ) { $rowcolor = 'RED'; if ($color_num % 2 > 0) { $rowcolor = 'GREEN'; } print REPORT_FILE qq( <tr bgcolor=\"$rowcolor\"> <td width=400 align=left>$data[0]</td> <td width=50 align=left>$data[1]</td> <td width=50 align=left>$data[2]</td> <td width=50 align=left>$data[3]</td> <td width=50 align=left>$data[4]</td> </tr> ) $color_num++; }
Re: alternating row colors
by blue_cowdawg (Monsignor) on May 18, 2001 at 17:38 UTC

    Disclaimer: I haven't tested this code but this should give you some ideas

    my @colors = [ qw ( red blue ) ]; foreach my $ix(0..$#data) { printf "<td><font color=\"%s\">%s</td>", $colors[($ix % 2)],$data[$ix]; }

    If it is the background color you are trying to modify substitute "<td>" with "<td bgcolor="%s">".

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Peter L. Berghold --- Peter@Berghold.Net
    "Those who fail to learn from history are condemned to repeat it."
    
Re: alternating row colors
by Anonymous Monk on May 19, 2001 at 06:00 UTC
    I like...
    $bgcolor1 = "#BOBOBO"; $bgcolor2 = "#B7DFF7"; print "<TABLE>\n"; while( @data = $query->fetchrow ) { foreach $line(@data) { if ($bgcount > 1) { $bgcolor=$bgcolor2;$bgcount=1; } else { $bgcolor=$bgcolor1;$bgcount=2; } print "<TR bgcolor=$bgcolor> <td width=50 align=left>$data[1]</td> <td width=50 align=left>$data[2]</td> <td width=50 align=left>$data[3]</td> <td width=50 align=left>$data[4]</td> </TR>"; } } print "</TABLE>\n";
Re: alternating row colors
by zane (Acolyte) on May 19, 2001 at 13:50 UTC
    I usually make a 2 element array, and store one color in each element. Then I have a flip-flop counter, that starts at zero or one, and at the end of the loop, is set to one minus itself (thus flip-flopping between 0 and 1), and then in the loop just refer to the counter's element of the array. It goes something like this:
    @colors = (black, white); $flipflop=0; while ($n < $nmax) { print("<tr color=\"$colors[$flipflop]\"><td>content</td></tr>"); $flipflop = 1 - $flipflop; }

    --
    If we don't change direction soon, we'll end up where we're going.