views:

441

answers:

3

Given something like

foreach (keys %myHash) {
   ... do stuff ...
}

foreach (keys %myHash) {
   ... do more stuff ...
}

Is Perl guaranteed to iterate over the keys in a consistent order if the hash is not altered?

+20  A: 

Yes. From perldoc -f keys:

The keys 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 perldoc perlsec).

(emphasis mine)

Adam Batkin
+1 It would be great if people at least attempted to read the excellent documentation that comes with Perl.
Sinan Ünür
A: 

This is a pretty dicey expectation. It probably will, but why worry? Fetch keys in advance, save the result, then iterate over the saved result. Then, you're guaranteed to access keys in same order. Working around the edges of unspecified implementation details is dangerous.

EDIT: Missed the "guarantee" in the doc, but I still think it's dangerous to expect this will never change. Especially when there are saner ways to achieve the same ends.

xcramps
Why the scare-quotes around "guarantee"? It's (a) very clearly stated; (b) unlikely to change, because its important for `keys` and `values` to return in the same order, so that the two can be correlated for when you can't use `each`.
derobert
A: 

Edit:

Although a normal hash has a consistent ordering, in the case of a tied hash the order of the keys is not well defined, as it is user-controlled!


Although the hash key order does not change, you probably should reconsider why you need to do this.

Perhaps you can process the hash in one pass instead of two?

You should save the hash keys into an array as a defensive programming practice, unless the size of the data is large enough that duplicating it would be a problem. As a bonus, you can even sort the list easily and process in the hash in a well defined order. E.g.,

   my @keys = sort keys %myHash;

This avoids any problems with modifying the hash, since your array order will never change unless you want it to.

If you do not do this, you need to be very careful not to do anything that changes the hash, otherwise the order of the elements will change. Look into the Readonly module to ensure that this hash is never modified.

Kevin Panko
I don't get it - why should you be defensive about a feature that's tested, documented and thus guaranteed to work?This seems just as silly as using a text editor and calling File -> Safe twice in the a row in the hope that if the first one fails silently, the second one will succeed.
moritz
@moritz: The issue isn't that Perl changes the key order (or not), it's that the order can change if the hash is modified. This answer is suggesting a defensive programming tactic (separate array plus Readonly) for future maintenance of the OP's codebase, and has nothing to do with Perl internals.
Andrew Barnett
Unless there are strong performance reasons not to, I try to always sort. Been bitten too often by code that implicitly expects a certain order and starts failing mysteriously when seemingly unrelated changes result in a different order of keys.
ysth