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

I am having an issue with some code I'm writing. I basically want to do the same process for a list of course names (which are all under topic names). I pulled the course names into a hash and the script works fine. The only problem is that there is no rhyme or reason to the order that these courses print out. Ideally, I want them to be ordered by topic name AND course name, though at this point, even having them ordered by topic name alone would be an achievement. I am very, very new to Perl, and I just read somewhere that hashes have no concept of order. Do arrays? I have just tried doing this in an erray, but am having problems with the foreach statement, I guess. Does anyone know how I can order these using a hash? If not, can anyone tell me how to change this into an array situation?? Here's the beginning of my code, up to the "foreach":
#!/usr/local/bin/perl5.005
use lib '../L2e/lib';
use DBI(0.90);
use strict;
use Database;
use CGI qw(:standard);

Database::->db_info_path("../L2e/lib/db.cfg");
my $dbh = new Database;

print "Content-type: text/html\n\n";

#Grab the evaluation responses from the form

my $tc9Questions = param('tc9Questions');
my $tcQ10 = param('tcQ10');
my $tcQ12 = param('tcQ12');

#Get the list of courses

my $title = "select C.il2e002_course_id_k, C.il2e002_course_title_x, 
T.il2e001_topic_id_k from il2e001_topic T, il2e002_course C 
where T.il2e001_topic_id_k = C.il2e001_topic_id_k order by T.il2e001_topic_desc_x, 
C.il2e002_course_title_x";

my %titles = $dbh->fetch_all_hash($title);

foreach my $key (keys %titles)
{

Replies are listed 'Best First'.
RE: Order a hash?
by mikfire (Deacon) on Jul 20, 2000 at 16:56 UTC
    Yes, hashes have no concept of order. Arrays do. The larger issue of your problem leads me to believe that hashes are the better data structure.

    You choice then is to sort the keys of the hash. You foreach loop ( which I write as a for loop - they are identical, so do not be confused ) would look something like:

    for my $key ( sort keys %title ) {
    which will sort the keys in asciibetical order. To sort the keys on some criteria other than asciibetical, try something like this ( I hope I remember how fetch_all_hash works ):
    # This will sort by title, then by topic id sub by_course_and_title { return $title{$a}{il2e002_course_title_x} cmp title{$b}{il2e002_cour +se_title_x} || $title{$a}{il2e002_topic_id_k} cmp title{$b}{il2e002_topic_id +_k}; } for my $key ( sort &by_course_and_title keys %title ) {

    In answer to your other question, how to step through an array, it is simply

    for my $foo ( @foo ) { # Do something interesting }
    See perldoc -f sort to understand a bit more of the possibilities of the sort command - it is quite potent.

    mikfire

Re: Order a hash?
by dempa (Friar) on Jul 20, 2000 at 17:18 UTC
    The answers above are probably enough to solve your problem, but just for reference, there's always the Tie::IxHash module. It provides hashes that preserve the order in which the elements were added. The elements can also be ordered in other ways. Look up the documentation for Tie::IxHash for a better explanation of all it's features.
RE: Order a hash?
by turnstep (Parson) on Jul 20, 2000 at 17:59 UTC

    A minor distinction - hashes do have a specific order, it is just that the order is internal to perl and usually makes no sense to us. (Perl does this on purpose, to make the hash lookup as fast and efficient as possible). More importantly, the order that a hash is in may look random, but it is the same random every time the hash is called (and as long as you don't change the hash). In other words, all calls to keys, values, and each will always return the elements of the hash (keys, values, or both) in the same order. Sorting, as mentioned above, is a great way to sort out your keys in a hash: "sort keys %hash" is a very common construct. Arrays are always ordered, but require you to access it by using a number. Hashes allow you to access it by any name you want, with the drawback of not having any sort of order, as you have discovered. Play with both, and try converting from one to another to get a good feel for them. There are lots of examples on this site, and of course, feel free to post questions here as well.

      Out of mild curiosity, does Perl do any caching of a sort keys %hash call, or do you have to do that yourself? (And if so, when does it become worth it? How many repeated calls of sort keys %hash does it take until my @sortedkeys = sort keys %hash becomes a worthwhile use of memory? 1? 2? Never? I don't think that I've ever written a script that needed to sort the same thing more then once, and if I did I probably cached the results in an array first, but I'm wondering if Perl does any optimization there...
(ar0n; use sort)RE: Order a hash?
by ar0n (Priest) on Jul 20, 2000 at 16:48 UTC
    foreach my $key (sort keys %titles) { # blah }

    Or, reversed:
    foreach my $key (reverse sort keys %titles) { #blah }

    Also, I believe there is a question in the Q&A section on this. Look there for more info.

    -- ar0n
Re: Order a hash?
by atl (Pilgrim) on Jul 20, 2000 at 20:24 UTC
    I'm wondering ... unless you do something with the hash after the foreach loop (not shown), why do you read everything into it in the first place?

    You could iterate over the single lines of what the database returns (at least using DBI 1.12, don't know about pre-1.0-versions), therefore preserving the order generated by the database (your "order by" statement):

    ... $dbh = DBI->connect("DBI:mysql:$database:$hostname", $user, $password) +; $statement = "select ... order by ..."; $sth = $dbh->prepare($statement) or die "Can't prepare $statement: $dbh->errstr\n"; $rv = $sth->execute or die "can't execute the query: $sth->errstr\n"; my @row; while(@row = $sth->fetchrow_array) { # @row contains the individual columns # ... do work ... } $rc = $sth->finish; $rc = $dbh->disconnect;
    8-)

    Andreas

Re: Order a hash?
by cynicgrrl (Initiate) on Jul 20, 2000 at 20:15 UTC
    Wow... thanks so much, everyone. I have the script working now (finally!). This is probably one of the friendlier forums I have seen. I appreciate everyone's help. :)