What decides the order of keys when I print a Perl hash?

14,875

Solution 1

Elements of a hash are printed out in their internal order, which can not be relied upon and will change as elements are added and removed. If you need all of the elements of a hash in some sort of order, sort the keys, and use that list to index the hash.

If you are looking for a structure that holds its elements in order, either use an array, or use one of the ordered hash's on CPAN.

the only ordering you can rely upon from a list context hash expansion is that key => value pairs will be together.

Solution 2

From perldoc -f keys:

The keys of a hash are returned in an apparently random order. The actual random order is subject to change in future versions of Perl, but it is guaranteed to be the same order as either the values or each function produces (given that the hash has not been modified). Since Perl 5.8.1 the ordering is different even between different runs of Perl for security reasons (see Algorithmic Complexity Attacks in perlsec).

...

Perl has never guaranteed any ordering of the hash keys, and the ordering has already changed several times during the lifetime of Perl 5. Also, the ordering of hash keys has always been, and continues to be, affected by the insertion order.

Also note that while the order of the hash elements might be randomised, this "pseudoordering" should not be used for applications like shuffling a list randomly (use List::Util::shuffle() for that, see List::Util, a standard core module since Perl 5.8.0; or the CPAN module Algorithm::Numerical::Shuffle), or for generating permutations (use e.g. the CPAN modules Algorithm::Permute or Algorithm::FastPermute), or for any cryptographic applications.


Note: since you are evaluating a hash in list context, you are at least guaranteed that each key is followed by its corresponding value; e.g. you will never see an output of a 4 b 3 c 2 d 1.

Solution 3

I went over your code and made some notes that I think you will find helpful.

use strict;
use warnings;

# declare a new hash and initialize it at the same time
my %some_hash = (
    foo   => 35,       # use the fat-comma or '=>' operator, it quotes the left side
    bar   => 12.4,     
    2.5   => "hello",
    wilma => 1.72e30,
    betty => "bye",    # perl ignores trailing commas, 
                       # the final comma makes adding items to the end of the list less bug prone.
);

my @any_array = %some_hash; # Hash is expanded into a list of key/value pairs.

print "$_ => $some_hash{$_}\n" 
    for keys %some_hash;

print "\n\n",              # You can print multiple newlines in one string.
      "@any_array\n\n";    # print takes a list of things to print.

# In print @foo; @foo is expanded into a list of items to print.  
# There is no separator between the members of @foo in the output.

# However print "@foo"; interpolates @foo into a string. 
# It inserts spaces between the members of the arrays.


# This is the block form of 'for'
for my $k (sort keys %some_hash)
{
    # Interpolating the variables into a string makes it easier to read the output.
    print "$k => $some_hash{$k}\n";
}

Hashes provide unordered, access to data by a string key.

Arrays provide access to ordered data. Random access is available by using a numerical index.

If you need to preserve the order of a group of values, use an array. If you need to look up members of the group by an associated name, use a hash.

If you need to do both, you can use both structures together:

# Keep an array of sorted hash keys.
my @sorted_items = qw( first second third fourth );

# Store the actual data in the hash.
my %item;
@item{ @sorted_items } = 1..4;  # This is called a hash slice.  
                                # It allows you to access a list of hash elements.
                                # This can be a very powerful way to work with hashes.

# random access
print "third => $item{third}\n";


# When you need to access the data in order, iterate over
# the array of sorted hash keys.  Use the keys to access the
# data in the hash.

# ordered access
for my $name ( @sorted_items ) {
    print "$name => $item{$name}\n";
}

Looking at your code samples, I see a couple of things you might want to work on.

  • how looping structures like for and while can be used to reduce repeated code.
  • how to use variable interpolation

BTW, I am glad to see you working on basics and improving your code quality. This investment of time will pay off. Keep up the good work.

Solution 4

The elements are (almost certainly) printed out in the order they appear (internally) in the hash table itself -- i.e. based on the hash values of their keys.

The general rule to follow is to use something other than a hash table if you care much about the order.

Solution 5

Hashes are not (necessarily) retrieved in a sorted manner. If you want them sorted, you have to do it yourself:

use strict;
use warnings;

my %hash = ("a" => 1, "b" => 2, "c" => 3, "d" => 4);

for my $i (sort keys %hash) {
    print "$i -> $hash{$i}\n";
}

You retrieve all the keys from a hash by using keys and you then sort them using sort. Yeah, I know, that crazy Larry Wall guy, who would've ever thought of calling them that? :-)

This outputs:

a -> 1
b -> 2
c -> 3
d -> 4
Share:
14,875
Nano HE
Author by

Nano HE

A newbie, China based; And I am new to programming. Work mostly with C#, C++, Perl & Php; Using OS: winxp; C#/C++: Visual Studio 2005/2008; Perl: Active Perl 5.10.1 + Komodo Edit 6; Php: NetBeans IDE 6.7 + XAMPP 1.7.3 Thanks for reading and for the replies! ;) I'm Reading Magic Tree House

Updated on June 14, 2022

Comments

  • Nano HE
    Nano HE almost 2 years

    activePerl 5.8 based

    #!C:\Perl\bin\perl.exe
    use strict;
    use warnings;
    
    # declare a new hash
    my %some_hash;
    
    %some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
          "wilma", 1.72e30, "betty", "bye\n");
    
    my @any_array;
    @any_array = %some_hash;
    
    print %some_hash;
    print "\n";
    print @any_array;
    print "\n";
    print $any_array[0];
    print "\n";
    print $any_array[1];
    print "\n";
    print $any_array[2];
    print "\n";
    print $any_array[3];
    print "\n";
    print $any_array[4];
    print "\n";
    print $any_array[5];
    print "\n";
    print $any_array[6];
    print "\n";
    print $any_array[7];
    print "\n";
    print $any_array[8];
    print "\n";
    print $any_array[9];
    

    Output as this

    D:\learning\perl>test.pl
    bettybye
    bar12.4wilma1.72e+030foo352.5hello
    bettybye
    bar12.4wilma1.72e+030foo352.5hello
    betty
    bye
    
    bar
    12.4
    wilma
    1.72e+030
    foo
    35
    2.5
    hello
    D:\learning\perl>
    

    What decided the elements print order in my sample code?

    Any rule to follow when print a mixed(strings, numbers) hash in Perl? Thank you.

    bar12.4wilma1.72e+030foo352.5hello
    

    [Updated]

    With you guys help, i updated the code as below.

    #!C:\Perl\bin\perl.exe
    use strict;
    use warnings;
    
    # declare a new hash
    my %some_hash;
    
    %some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
          "wilma", 1.72e30, "betty", "bye");
    
    my @any_array;
    @any_array = %some_hash;
    
    print %some_hash;
    print "\n";
    print "\n";
    print @any_array;
    print "\n";
    print "\n";
    
    my @keys;
    @keys = keys %some_hash;
    for my $k (sort @keys)
    {
        print $k, $some_hash{$k};
    }
    

    output

    D:\learning\perl>test.pl
    bettybyebar12.4wilma1.72e+030foo352.5hello
    
    bettybyebar12.4wilma1.72e+030foo352.5hello
    
    2.5hellobar12.4bettybyefoo35wilma1.72e+030
    D:\learning\perl>
    

    Finially, after called keys and sort functions. The hash keys print followed the rule below

    2.5hellobar12.4bettybyefoo35wilma1.72e+030