This script generates a web page with all my CD's grouped by one of several paramaters. The input file looks something like this:

# this line is a comment
# fields are delimited by ','
# field order is: artist, category, year, title, notes
Van Halen,80's rock,1983,1984,Go Eddie!
Sheryl Crow,90's pop,1993,Tuesday Night Music Club,...

This is awfully similar to transpose AoA and HTML table AoA contents., but I've been working on this for a while and wanted to get some feedback. It's different in that I'm loading my data into HoAoA (hashes of arrays of arrays). Not only that, but a couple of days ago I asked about Using $_ in nested loops, which you will see came from this script.

#!perl -wT # musiclist.cgi -- spits out my CD collection as HTML; 2nd attempt use strict; use CGI; # some security, as per the CGI.pm POD $CGI::POST_MAX = 1024 * 0; # no posts $CGI::DISABLE_UPLOADS = 1; # no uploads #################### my ($infile, @data, $description, $artist, $category, $year, $title, $notes, $numFields, %groupBy, $q, $key); $description = <<END_DESCRIPTION; About a year ago I thought it would be cool to make a dynamically sort +able list of all the CD's, cassettes, and LP's in my collection. I got as far as + entering the information about my CD's, which consists of the following: title, + group or artist, copyright year, what "category" the CD falls into, and notes. END_DESCRIPTION $infile = 'music.txt'; @data = (); # columns/indices of fields of input file ($artist, $category, $year, $title, $notes) = (0, 1, 2, 3, 4); $numFields = 5; # mapping table; index of sort key must be first element in array %groupBy = ( artist => [ $artist, $category, $year, $title, $notes ], category => [ $category, $artist, $year, $title, $notes ], year => [ $year, $artist, $category, $title, $notes ], title => [ $title, $artist, $category, $year, $notes ], ); #################### $q = new CGI; $key = $1 if $q->param('groupKey') =~ /^(\w+)$/; print $q->header, $q->start_html("My CD's"), $q->h1("My CD's"); if( $key && exists $groupBy{$key} ) { my %groups; print $q->hr, $q->p("Here is my collection of CD's, grouped by ", $key, ".") +; open IN, "<$infile" or die "Error opening input file: $!"; while( <IN> ) { next if /^#|^$/; # skip commented and empty lines chomp; push @data, [ split ',', $_, $numFields ]; } close IN; # change this if mapping table (%groupBy) changes for ( @data ) { my @fields = @{ $groupBy{$key} }; push @{ $groups{ @{ $_ }[ $fields[0] ] } }, [ @{ $_ }[ $fields[1] ], @{ $_ }[ $fields[2] ], @{ $_ }[ $fields[3] ], @{ $_ }[ $fields[4] ], ]; } for ( sort keys %groups ) { my @entries; for ( @{ $groups{$_} } ) { push(@entries, join(", ", @{ $_ })); } print $q->p($q->b($_)), $q->ul($q->li(\@entries)); } } else { print $q->p($description); } print $q->hr, $q->startform("GET", "musiclist.cgi", $CGI::URL_ENCODED), $q->p('Group by:'), $q->radio_group( -name => 'groupKey', -values => [ 'category', 'artist', 'year', 'title' ], -labels => { category => 'Category', artist => 'Artist, group, or composer', year => 'Copyright year', title => 'CD title', }, -linebreak => 'true' ), $q->br, $q->submit, $q->reset; $q->endform, $q->hr, $q->end_html;

Comments or suggestions are appreciated, especially anything along the lines of alternate ways of handling nested data structures.

Update: Sorry for being so ungracious...my mother taught me better than that! :-) Everything (almost) that I do in this script related to CGI programming that is good I learned from Ovid's Web Programming using Perl course.

Update: (19 Jan. 2001) I decided to go through the script line by line, not unlike merlyn's articles, and explain each step, and while doing this noticed that I could do some things more efficiently. (I even learned how to use Benchmark to prove that it's improved.)

The majority of the processing takes place in the block begun with if( $key && exists $groupBy{$key} ). The original version contains three "loops": 1)while( <IN> ), 2) for( @data ), and 3) for( sort keys %groups ).

As I carefully thought about what was really happening in these loops, I realized I could combine the first two into one.

while( <IN> ) { next if /^#|^$/; chomp; my @record = split ',', $_, $numFields; push @{ $groups{ $record[ $fields[0] ] } }, [ $record[ $fields[1] ], $record[ $fields[2] ], $record[ $fields[3] ], $record[ $fields[4] ], ]; } close IN;

Instead of looping over each line of input and pushing the fields into an array of arrays, and then using a separate loop (over the array of arrays) to push the data into a hash of arrays of arrays, I put the data into the hash of arrays of arrays in one step (one loop, rather).


In reply to Look at my CD's! by t'mo

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.