How can I sort a hash's keys naturally?

2019-03-24 16:17发布

问题:

I have a Perl hash whose keys start with, or are, numbers.

If I use,

foreach my $key (sort keys %hash) {
    print $hash{$key} . "\n";
}

the list might come out as,

0
0001
1000
203
23

Instead of

0
0001
23
203
1000

回答1:

foreach my $key (sort { $a <=> $b} keys %hash) {
    print $hash{$key} . "\n";
}

The sort operation takes an optional comparison "subroutine" (either as a block of code, as I've done here, or the name of a subroutine). I've supplied an in-line comparison that treats the keys as numbers using the built-in numeric comparison operator '<=>'.



回答2:

Paul's answer is correct for numbers, but if you want to take it a step further and sort mixed words and numbers like a human would, neither cmp nor <=> will do. For example...

  9x
  14
  foo
  fooa
  foolio
  Foolio
  foo12
  foo12a
  Foo12a
  foo12z
  foo13a

Sort::Naturally takes care of this problem providing the nsort and ncmp routines.



回答3:

$key (sort { $a <=> $b} keys %hash) 

will do the trick

or

$key (sort { $b <=> $a} keys %hash)

descending sort

or even

$key (sort { $a <=> $b} values %hash)
$key (sort { $b <=> $a} values %hash)


回答4:

Your first problem is the body of the loop (which no other answer here seems to point out).

foreach my $key ( sort keys %hash ) {
    print $hash{$key} . "\n";
}

We don't know what the keys of %hash are, we just know that they that are handed to you as $key, in lexical order, inside the loop. You then use the keys to access the contents of the hash, printing each entry.

The values of the hash do not come out in a sorted order, because you sort on the keys.

Would you instead want to output the values in sorted order, consider the following loop:

foreach my $value ( sort values(%hash) ) {
    printf( "%s\n", $value );
}

This loop does print the values in the order you observe:

0
0001
1000
203
23

To sort them numerically instead, use

foreach my $value ( sort { $a <=> $b } values(%hash) ) {
    printf( "%s\n", $value );
}

This produces

0
0001
23
203
1000

which is what you wanted.

See the Perl manual for the sort function for further information and many more examples.