简体   繁体   中英

Read hashes in Perl based on keys

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 ie 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?

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).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM