I have keys in a hash as follows: AB3, AB1, AB2
and so on. I would like to sort the hash by the keys. How do I do this in perl?
A related issue is how can I sort the keys so that letters and numbers appear in the correct order?
I have keys in a hash as follows: AB3, AB1, AB2
and so on. I would like to sort the hash by the keys. How do I do this in perl?
A related issue is how can I sort the keys so that letters and numbers appear in the correct order?
You need the sort
function:
foreach my $key ( sort keys %hash ) {
print "$key => $hash{$key}\n";
}
You can't easily maintain hashes as sorted structures, as they just don't work that way. See: perldata
for more about how hashes work.
Edit: One of the niceties of sort
in perl though, is that it lets you specify a function. This function should take $a
and $b
and return -1
, 0
, +1
depending on if they're before or after.
cmp
does that for alphabetical. <=>
does that numerically. And best of all, when combined with ||
you can daisy chain the criteria, because -1
or 1
are 'true' but 0
is false.
Something like this for example (borrowed the key list from another post to illustrate):
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my @keys = qw/ AB3 AB1 AB4
CD5 CD107 CB8
AC1 AC5 AC33
BA84 CB11 CA233/;
sub lex_num {
#split the keys into "word" and "digit" elements.
my @a_keys = $a =~ m/([A-Z]+)(\d+)/i;
my @b_keys = $b =~ m/([A-Z]+)(\d+)/i;
return ( $a_keys[0] cmp $b_keys[0]
|| $a_keys[1] <=> $b_keys[1] );
}
print join( "\n", sort lex_num @keys );
This will sort the first bit lexically, the second bit numerically. If you've more combinations of letters and numbers then this won't work, but you can do e.g. a split
and a for
loop.
If your keys are defined by non digits followed by digits, you could do:
my %h = (
CD45 => 4,
AB1 => 1,
AB22 => 3,
AB5 => 2,
);
sub mySort {
my ($xa,$ya) = $a =~ /^(\D+)(\d+)$/;
my ($xb,$yb) = $b =~ /^(\D+)(\d+)$/;
return -1 if $xa lt $xb;
return +1 if $xa gt $xb;
return $ya <=> $yb;
}
for (sort { mySort } keys %h) {
say "$_ => $h{$_}";
}
Output:
AB1 => 1
AB5 => 2
AB22 => 3
CD45 => 4
Here's how you might combine the two approaches and shorten your code by using a CPAN module - in this case Sort::Naturally
(I have made the hash keys a bit more complex for illustrative purposes):
use Sort::Naturally ;
my @keys = qw/ AB3 AB1 AB4
CD5 CD107 CB8
AC1 AC5 AC33
BA84 CB11 CA233/ ;
# make a hash from the keys with "whatever" as value:
my %hash;
%hash = map { $_ => $hash{$_} = 'whatever' } @keys ;
# Auto-magically naturally sort alphanumerically
# A module based approach to @Toto's solution:
for ( nsort keys %hash ) { print "$_ => $hash{$_} \n" };
@Sobrique has responded with the basic perl approach to sorting a hash by its keys, and @Toto has shown how you can sort those keys in a specified order using your own subroutine as an argument in the block { }
following the builtin sort()
function.
References:
perldoc
perldsc
)