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).
|
|---|