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

I've thrown together a web front end for a database of terms that some users have asked me to compile for them. The database is very small, and consists of id,term,description.

The front end simply builds a list of the terms with named anchors that link to the full summary of the term lower in the page. It basically looks like this:

Pacifier Pacify Package Packing Pacifier | top | Edit Description and summary of Pacifier Pacify | top | Edit Description and summary of Pacify Package | top | Edit Description and summary of Package Packing | top | Edit Description and summary of Packing

So clicking on Pacifier with the named anchor, will bring you to the section summary for Pacifier. So far, so good...

What has been suggested, since the list of terms can get very long, is to create some named anchors which will "warp" to specific sections in the page. For example, <a href="#Pa">Pa</a> would jump you to the part of the page that contains terms that start with "Pa". <a href="#Pe">Pe</a> would jump lower to the section of terms beginning with "Pe", etc.

I can cut down the amount of vertical scrolling through a long list, by making them "Pa", "Pe", "Pi", "Po", "Pu", for each letter of the alphabet ($letter . [aeiou];)

The problem I'm having, is that my query is in a $sth->fetch() loop that prints each term out of the database. In this construct, how do I "slice" each term coming out, and determine when the first two letters change from "Pa" to "Pe", and set a named anchor directly before the change?

I can't quite figure out the logic to make that happen, programmatically.

Replies are listed 'Best First'.
Re: Programmatically building named anchors to warp to sections
by mreece (Friar) on Aug 15, 2007 at 01:22 UTC
    assuming your rows are sorted by name, you just need to get the relevant substring(), compare it to the 'current' substring, and print out an anchor when the new substring doesn't match the current one.
    my $section = ''; while (my $row = $sth->fetchrow_hashref) { my $prefix = substr($row->{name}, 0, 2); if ($prefix ne $section) { $section = $prefix; print qq{<a href="#$section">$section</a>}; } # do the usual }
    instead of printing, you'll probably want to append the content to a variable, and push each $section into a @list, so you can then print the navigation header before printing the output buffer.

      As mr_mischief pointed out, your compare starts a new section on EVERY change of the second letter...which is just a frog-hair off plumb from hacker's schema:

      For example, this could make the number of <a name="... /a><c>s and <c><a href="... /a>s very large for a list like:

      mreece also makes important points. Please consider them.

      Your code makes a new section for every change in the second letter if the name. hacker wanted just to have sections at the vowels ( first letter . [aeiou] ).

      Also, I'm not sure what's wrong with printing as the output comes, either, instead of building up an array.

      Update: added code tags around character class.
        if hacker really only wants the breaks only on vowels, it is an easy change. (if ($prefix =~ /[aeiou]$/ and $prefix ne $section) { ...?). but it begs the question, what is the first anchor? no anchors at all until you encounter a vowel in the second position? i was hoping to provide a hint to the right direction (substr), without providing a complete solution, because there are a lot of unknowns here!

        the only issue with buffering versus printing is if you want to print the navigational anchor links before the content, without assuming that every anchor will actually exist. it may be best to first determine which anchors will actually be present, then print the navigational links, then the content.

Re: Programmatically building named anchors to warp to sections
by mr_mischief (Monsignor) on Aug 15, 2007 at 01:33 UTC
    I just submitted this a couple of minutes ago:
    Better yet is this:
    use strict; use warnings; my @p_results = ( { 'title' => 'Package' }, { 'title' => 'Paddle' }, { 'title' => 'Peck' }, { 'title' => 'Pessimist' }, { 'title' => 'Pickle' }, { 'title' => 'Piston' }, # { 'title' => 'Pocket' }, { 'title' => 'Pout' }, { 'title' => 'Pride' }, { 'title' => 'Promise' }, { 'title' => 'Pucker' }, { 'title' => 'Putter' }, ); my @updates = qw( a e i o u ~ ); my $i = 0; foreach my $item ( @p_results ) { if ( substr( $item->{'title'}, 1, 1 ) ge $updates[$i] ) { print "\n-= P" . $updates[$i] . " =-\n\n"; $i++; } print $item->{'title'} . "\n"; }

    This new one is better in two ways. It handles the case that you never hit exactly the letter your header contains. It also doesn't have an extra variable that's never used. I caught that without "use warnings;", but I added those to the second one after I caught it.

    See if you can figure out really quickly why there's an extra character in the @updates array the second time around.

    Update 2: fixed a tpyo (in the exposition, not in the code).
Re: Programmatically building named anchors to warp to sections
by CountZero (Bishop) on Aug 15, 2007 at 06:38 UTC
    If you really have a lot of words starting with the same letter, then chances are good that you will effectively have a word at every one of your intermediary jump stations.

    So I would not bother with a scheme which has to go twice through your list in order to build-up a navigation panel. It is a waste of cycles and memory. Just provide a navigation panel for all of "Pa", "Pe", "Pi", "Po", "Pu". It may be there is no item starting with "Po", but still include the anchor for "Po" where it should have been if there was a word with "Po". Perhaps there is a word starting with "Pr" and then your link is still quite close to the mark!


    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James