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
- cf.
perlfaq
-q How can I always keep my hash sorted? - In addtion to creating a number of really useful CPAN modules, GARU (Breno de Oliveira) published an excellent article on hash ordering that thoroughly covers recent Perl development and hash randomization issues.
- For more advanced examples of neat things you can do with hash slices see Vince Veselosky’s article Hash slices can replace loops.