http://www.perlmonks.org?node_id=268562

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

Just a quick question for the Perlmonks.

I have a hash, with keys being month names and values being number of days in the month.

If I want to print the keys out in a *non-alphabetical* order (if I sort them then I get "April", "August", "December", "February" etc.) particularly the order in which they come in - (Jan to Dec) how can I do this? Or am I going to end up with another hash holding the month, and an integer representation to refer to in the sort?

I know hash entries are effectively random when set up based on memory location, but there must be some trick to ordering the hash keys as I wish.. I had a look at Date::* but nothing lept out at me

Many thanks

Bukowski - aka Dan (dcs@black.hole-in-the.net)
"Coffee for the mind, Pizza for the body, Sushi for the soul" -Userfriendly

  • Comment on Sorting hash keys according to different criteria

Replies are listed 'Best First'.
•Re: Sorting hash keys according to different criteria
by merlyn (Sage) on Jun 24, 2003 at 15:58 UTC
    If they are coming in in the order that you need them, consider Tie::IxHash to keep the hash keys in the order of assignment.

    Or, just have an array with the 12 months in it, and use that rather than sorting the keys. Skip over the elements that don't exist.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      That, will do nicely :) ++ and thanks!

      Bukowski - aka Dan (dcs@black.hole-in-the.net)
      "Coffee for the mind, Pizza for the body, Sushi for the soul" -Userfriendly

Re: Sorting hash keys according to different criteria
by Bilbo (Pilgrim) on Jun 24, 2003 at 15:57 UTC

    If you want to put them in order (Jan, Feb, Mar...) you're going to have to tell it what that order is somewhere. I'd have an array:

    @months = qw(January February March ... );

    Then to access the months in order do

    foreach my $month {@months} { print "$month has $days{$month} days\n"; }
Re: Sorting hash keys according to different criteria
by George_Sherston (Vicar) on Jun 24, 2003 at 17:19 UTC
    Depending on what you're doing with this you might find an array of hashes useful. This would give you the data in the fixed order ordained by the calendar - you could then pull it out however you liked, and even have a framework for adding more info.
    my @months = ( { name => "January", days => 31, }, { name => "February", days => 28, # or whatever... there's another prob },
    etc etc etc

    You could then use the month number returned by localtime to pull data out of this array.

    Having said that, if you're doing a lot of date calculations, then half an hour rummaging around in CPAN would probably save you time.

    § George Sherston
      Hi, Can I ask why this only prints one item in the hash array?
      my %people; %people =( { name => "fred", age => 31, }, { name => "bill", age => 32, }); my $length = keys %people ; print "$length\n"; foreach my $key (sort keys %people) { print("$people{$key}{'name'}: $people{$key}{'age'}\n"); }

      Code tags - dvergin 2003-06-24

        It's because you've mixed up a hash of hashes with an array of hashes. %people is clearly a hash; but the thing after the = is an array of two hashes. But since, in Perl, hashes are a special kind of array, Perl just took you at your word that you wanted to treat this array as a hash.

        The end result is, %people is a hash with one key-value pair: the key is { name => "fred", age => 31, } and the value is { name => "bill", age => 32, }! I know that's not what you wanted, but it's what you told Perl to do!

        You'd get the output you want with
        my @people = ( { name => "fred", age => 31, }, { name => "bill", age => 32, } ); for (@people) { print "$_->{'name'}: $_->{'age'}\n"; }
        By the way, to make your posts easier to read, you can enclose your code with <code> tags!

        § George Sherston
Re: Sorting hash keys according to different criteria
by pzbagel (Chaplain) on Jun 24, 2003 at 16:09 UTC

    Short answer is no, long answer is maybe. No, there is no way to "order the hash". As it is written, so it shall be done. But seriously, the docs aren't lying to you, Perl stores it hashes the way it wants. No guarantee of ordering when you retrieve them.

    Now I say maybe because you may feel free to write your own sorting algorithm to sort then on the fly when you want to print them out. Some clever use of the Time::localtime module to convert the month name into a number. However, since you are dealing with 12 pieces of data, that seems like overkill. Honestly, an array with the Names of the months as elements would work perfectly for your needs.