Reputation: 1161
I have an hash in Perl as below. There are:
%typeMethodsMap = (
CHECK_REP_EXISTS => "1_abc",
CHECK_JDK_VERSION => "2_abc",
CHECK_BLOCKS_FAILED => "1_xyz",
CHECK_OR_EXISTS => "2_xyz",
CHECK_UPG_EXISTS => "3_xyz",
CHECK_SSO_EXISTS => "4_xyz"
);
When the hash is read, the keys are not read as defined but are read randomly. I needs to read and run through the loop on the hash based on the ascending format of the keys i.e. CHECK_BLOCKS_FAILED
, followed by CHECK_OR_EXISTS
followed by CHECK_UPG_EXISTS
and CHECK_SSO_EXISTS
for value "1_xyz"
, "2_xyz"
, "3_xyz"
and "4_xyz"
respectively.
Please let me know if any body can help me here?
Upvotes: 0
Views: 90
Reputation: 53508
Yes. By design, hash keys are random order.
There's a bunch of reasons for that - covered in perlsec
and keys
- but the long and short of it is if you need to preserve key ordering, you need to use sort
.
Or a slice
:
my @order = qw ( first second third );
my %hash = ( second => 'a', third => 'b', first => 'c' );
print "@hash{@order}";
Or:
foreach my $key ( @order ) {
print "$key = $hash{$key}\n";
}
Arrays are explicitly ordered numerically. Hashes are explicitly unordered (or random order).
If you're custom sorting, then you can use any function you like that returns -1, 0 or 1 based on the value of the comparison.
cmp
does this for strings, and <=>
does this for numbers.
Notes for custom sorting, it might look like this:
use strict;
use warnings;
use Data::Dumper;
my %typeMethodsMap = (
CHECK_REP_EXISTS => "1_abc",
CHECK_JDK_VERSION => "2_abc",
CHECK_BLOCKS_FAILED => "1_xyz",
CHECK_OR_EXISTS => "2_xyz",
CHECK_UPG_EXISTS => "3_xyz",
CHECK_SSO_EXISTS => "4_xyz",
);
my @order = qw(
CHECK_REP_EXISTS
CHECK_JDK_VERSION
CHECK_BLOCKS_FAILED
CHECK_OR_EXISTS
CHECK_UPG_EXISTS
CHECK_SSO_EXISTS
);
my $count = 0;
my %magic_order = map { $_ => $count++ } @order;
print Dumper \%magic_order;
sub custom_sort {
return $magic_order{$a} <=> $magic_order{$b};
}
foreach my $key ( sort { custom_sort } keys %typeMethodsMap ) {
print $key,"\n";
}
Although note - this isn't much more efficient, it's merely intended to illustrate 'custom sorting'. Alternatively - if you're wanting to sort based on your 'keys' being sorted:
sub custom_sort {
my ( $a_number, $a_text ) = split ('_',$a);
my ( $b_number, $b_text ) = split ( '_', $b );
if ( $a_number == $b_number ) {
return $a_text cmp $b_text;
}
else {
return $a_number <=> $b_number
}
}
This will sort numerically first, and then alphabetically second. (Swap the <=>
and cmp
if you want the opposite).
Upvotes: 4
Reputation: 126762
If you know what the keys are then you can write just
for my $key (qw/ 1_CHECK_BLOCKS_FAILED 2_CHECK_OR_EXISTS 3_CHECK_UPG_EXISTS /) {
...
}
Otherwise you must either keep track of the order of the keys in a separate array when you are building the hash, or use something like the Tie::Hash::Indexed
module (there are several similar ones) which maintains the order of hash data
Upvotes: 0