Reputation: 927
I've seen many times the following piece of code to join a hash to another hash
%hash1 = ('one' => "uno");
%hash2 = ('two' => "dos", 'three' => "tres");
@hash1{keys %hash2} = values %hash2;
I thought that every time the "values" or "keys" function is called, their output order was random. If this is true, how does the statement above gets the keys and values in the right order on both sides?
In other words, why there is not a chance of getting 'two' => 'tres' in %hash1 after merging both hashes? Is Perl smart enough to know that if "keys" and "values" are called on the same line, then keys and values must be given in the same order?
Upvotes: 9
Views: 721
Reputation: 386501
A hash is an array of linked lists. A hashing function converts the key into a number which is used as the index of the array element ("bucket") into which to store the value. More than one key can hash to the same index ("collision"), a situation handled by the linked lists.
The iterator used by keys
, values
and each
returns the elements in a order consistent with their location in the hash. I imagine it iterates over the linked list in the first bucket, then over the linked list in the second bucket, etc. The points is that it doesn't randomize the order in which it iterates over the elements of the hash. That's why the docs guarantee the following:
So long as a given hash is unmodified you may rely on keys, values and each to repeatedly return the same order as each other.
What is "random"[1] is in which bucket number to which a key will hash. Each hash has a random secret number that perturbs the hashing function. This causes the order of the elements in a hash to be different for each hash and for each run of a program.[2]
Adding elements to a hash can cause the number of buckets to increase, and it can cause trigger the secret number to change (if one of the linked lists becomes abnormally long). Both of these will change the order of the elements in that hash.
$ perl -le'
my %h1 = map { $_ => 1 } "a".."j";
my %h2 = map { $_ => 1 } "a".."j";
print keys(%$_) for \%h1, \%h1, \%h2, \%h2;
'
hjfeadbigc
hjfeadbigc
bdgcifjhae
bdgcifjhae
$ perl -le'
my %h1 = map { $_ => 1 } "a".."j";
my %h2 = map { $_ => 1 } "a".."j";
print keys(%$_) for \%h1, \%h1, \%h2, \%h2;
'
dcahigjbfe
dcahigjbfe
gihacdefbj
gihacdefbj
Upvotes: 5
Reputation: 4346
See perldoc -f keys
So long as a given hash is unmodified you may rely on keys, values and each to repeatedly return the same order as each other.
Upvotes: 13