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

I'm sure this is a simple question but how do I sort the keys of a hash in numeric order if the keys are of the form Drive0, Drive1 ... Drive10, Drive11, etc.

I have tried the following:

sub mysort { $a <=> $b; } foreach $i ( sort mysort ( keys %myhash ) ) { print "$i\n"; }

This only complains that Drive1 is not numeric.

If I use the standard ASCII sorting then it gets sorted Drive1, Drive10, Drive11, Drive2, etc.

Thanks in advance for the help.

Gorio

Replies are listed 'Best First'.
Re: "numeric" sort of keys?
by Zaxo (Archbishop) on Sep 25, 2003 at 17:59 UTC

    You need to remove 'Drive' from the comparison so that perl can make a number from it. sub mysort { substr($a,5) <=> substr($b,5) } That could probably benefit fram a Schwartzian Transform.

    After Compline,
    Zaxo

      This is exactly what I asked for BUT, I left out some detail on the structure of the keys. The text "field" of the key can vary in length and it is not always the word "Drive". My current list has Drive, IRMA, CMS, PRD and some others as the first "field" of the key and one or two digits as the second "field" of the key. The key is a combination of text and numbers and I want to sort first on the text "field" (which is not unique) and then sort on the numeric "field" of the key. Your help has pointed me in the right direction though. I see how I can iterate through each character until I find the first numeric character and calculate which column to split on. Or can I do this with some sort of regex and the split command? I'll go work it out and post my solution. Thanks to all for the help!!!!

      Gorio
        Gorio3721,
        My first attempt at the Schwartzian Transform.
        #!/usr/bin/perl -w use strict; my @keys = qw (CMS5 Drive1 Drive2 Drive10 IRMA1 IRMA13 IRMA2); my %hash; @hash{@keys} = (); for my $key ( custom_sort(\%hash) ) { print "Key: $key\n"; } sub custom_sort { my $hash = shift; return map { $_->[0] } sort { $a->[1] cmp $b->[1] || $a->[2] <=> $b->[2]} map { [ $_, /(\w+?)(\d+)$/ ] } keys %$hash; } __END__ Key: CMS5 Key: Drive1 Key: Drive2 Key: Drive10 Key: IRMA1 Key: IRMA2 Key: IRMA13

        Cheers - L~R

      Oh, one more thing.

      What the heck is a Schwartzian Transform?

      Thx,

      Gorio
Re: "numeric" sort of keys?
by davido (Cardinal) on Sep 25, 2003 at 18:08 UTC
    If you want to sort the keys by the numeric value that comes after the literal alpha characters, 'Drive', you could do this:

    @array = sort { substr( $a, 5 ) <=> substr ( $b, 5 ) } @array;

    That's will tell sort to look at the 6th character (offest 5 from zero) of the key, and everything thereafter, which in your example is a numeric string. Perl coerces the numeric string into a numeric value when used in the context of <=>, if there are no non-number characters present.

    That method assumes that 'Drive' always comes first. You'll have to look a little deeper into the perldocs for examples of how to sort by multiple criteria if you want to get more complicated than that.

    Update: Looks like Zaxo was a faster typist than I and beat me to the same answer. His makes use of sort's ability to utilize an alternate subname to redefine the comparison mechanism, whereas mine makes use of sort's ability to utilize a block that works as an anonymous subroutine that can redefine the built-in comparison mechanism. Both are essentially the same thing with different presentation in this context.

    Dave

    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein

Re: "numeric" sort of keys? (natural)
by tye (Sage) on Sep 26, 2003 at 04:15 UTC

    A search on natural sort finds several nice solutions for this kind of thing.

                    - tye (Yes, I wrote most of them. So?)
A reply falls below the community's threshold of quality. You may see it by logging in.