perlsortingdata-structureshashperl-data-structures

Why do hash keys have different order when printing?


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.


Solution

  • 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