I want to build several hashes using the same keys and for the keys to have the same order when I print them. So, in the example below, the keys of $hash1
and $hash2
should always have the same order, but there should be no need to keep that order when creating the hash.
use Data::Dumper;
my $hash1 = {
keyc => 2,
key1 => 1,
keya => 3,
keyb => 4,
};
my $hash2 = {
keyc => 2,
key1 => 1,
keya => 3,
keyb => 4,
};
print Dumper $hash1, $hash2;
But the output is as follows:
$VAR1 = {
'key1' => 1,
'keyc' => 2,
'keyb' => 4,
'keya' => 3
};
$VAR2 = {
'keyb' => 4,
'keya' => 3,
'keyc' => 2,
'key1' => 1
};
i.e the hashes have a different and unexpected order. What's wrong with my perl?
My perl version is:
This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)
Notice: I know that keys of perl hash is unsorted order. I want they have the same order, but there should be no need to have the sorted order. I hope that I can get the same print output if I run the code again.
Following advice from answers, I set two environment variables:
PERL_HASH_SEED=0x00 PERL_PERTURB_KEYS=0
Then I can get the same output when I run the code repeatedly.
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 inperl-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. TheHash::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 "
zxy
" not "zyx
". To impose our arbitrary version of order on this hash we first need to recognize that culprit here iskeys %hashed
which returns the keys in random order. The solution is tosort
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: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; asperldata
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
perlfaq
-q How can I always keep my hash sorted?Nothing wrong with your perl, a hash is unsorted.
If you want to sort by key you need to do something like that:
and same thing for hash2...
G. Cito's answer is correct. If you want sorted output from Data::Dumper however, you can do: