Why do hash keys have different order when printing?

When printing a hash there are a few different notions of order that are relevant: “insertion order”, “sort order” and “random”. See the ENVIRONMENT section of the perlrun documentation for a discussion of ways you can control this behavior and for the reasons why the default is to use hash randomization.

For at least a decade hashes in perl have not guaranteed key order. More recently, hash randomization has been part of a general security “hardening” effort. There are good reasons for hashes to be randomized. For more details see the perlsec discussion of algorithmic complexity attacks. You’ll note in the Perl security documentation that further enhancements were added in perl-5.18 – if you are seeing a different behavior compared to previous versions it may be due to these most recent changes.

Besides explicitly sorting your hash keys in a deterministic way, there are other approaches you can take to ordering your hashes: Hash::Ordered is one example. The Hash::Ordered documentation has a good discussion of the pros and cons of a number of other modules.

While a hash is an “unordered basket” of scalars arranged in key-value pairs; an array is an “ordered sequence” of scalars [1]. A “slice” is way of accessing “several elements of a list, an array, or a hash simultaneously”. A slice uses the @ sigil since the operation returns a list of multiple values – and with @ we get “ordered sequence”. The upshot is that one way to impose a kind of “order” on a hash is by using a slice to access it:

# We want alphabetical disorder ...
my %hashed = ( 1 => "z", 2 => "x", 3 => "y" );
for my $key ( keys %hashed ) { print $hashed{$key} } ;
__END__    
zyx

We want “zxy” not “zyx“. To impose our arbitrary version of order on this hash we first need to recognize that culprit here is keys %hashed which returns the keys in random order. The solution is to sort keys of ccurse and in this contrived example we store them in @sort_order and use it to “slice” out what we want from the hash, the way we want it:

my @sort_order = sort keys %hashed ;
print @hashed{@sort_order} ;
__END__
zxy

Tada!! Slices can be useful when you want to store keys and values in a hash but access that data in an ordered way. Remember the “@” when you want to slice a hash; as perldata puts it: “you use an '@' … on a hash slice … [because] you are getting back …a list”. And lists are orderly.


[1] The definitions of hashes as “unordered baskets” and arrays as “ordered sequence” are from Mike Friedman’s (FRIEDO) excellent article on Arrays vs. Lists in Perl.

Further References

Leave a Comment