tags:

views:

909

answers:

6

How can I access the last element of keys in a hash without having to create a temporary array?

I know that hashes are unordered. However, there are applications (like mine), in which my keys can be ordered using a simple sort call on the hash keys. Hope I've explained why I wanted this. The barney/elmo example is a bad choice, I admit, but it does have its applications.

Consider the following:

my %hash = ( barney => 'dinosaur', elmo => 'monster' );
my @array = sort keys %hash;
print $array[$#{$hash}];
#prints "elmo"

Any ideas on how to do this without calling on a temp (@array in this case)?

+5  A: 
print( (keys %hash)[-1]);

Note that the extra parens are necessary to prevent syntax confusion with print's param list.

You can also use this evil trick to force it into scalar context and do away with the extra parens:

print ~~(keys %hash)[-1];
friedo
Which explains why $#{$hash} returns -1 in the first place. Thanks for the tip.
Zaid
`%hash` and `$hash` are completely different variables. `$#{$hash}` returns -1 because it autovivifies `$hash` as a reference to an anonymous array. Since that array is empty, the index of the (nonexistent) last element is -1.
Michael Carman
To clarify on friedo's answer: this prints the value of the last key the iterator would return (in hash order). It does *not* print the last key inserted.
Michael Carman
Another trick how to avoid print parameter braces: print +(keys %hash)[-1];
Hynek -Pichi- Vychodil
+2  A: 

Hashes are unordered, so there is no such thing as the "last element." The functions for iterating over a hash (keys, values, and each) have an order, but it's not anything that you should rely on.

Technically speaking, hashes have a "hash order" which is what the iterators use. Hash order is dependent on the hashing algorithm, which can change (and has) between different versions of Perl. Moreover, as of version 5.8.1 Perl contains hash randomization features that can change the hashing algorithm in order to prevent certain types of attacks.

In general, if you care about order you should be using an array instead.

Michael Carman
@Michael: In general, but not always...
Zaid
@Zaid: Arrays preserve insertion order, hashes don't. Sorting isn't the same thing as it creates a new ordering.
Michael Carman
A: 

According to perldoc perldata:

Hashes are unordered collections of scalar values indexed by their associated string key.

Since hash are unordered. So, sorry. There are no "last" element.

J-16 SDiZ
A: 

Hashes are unordered element. so might be your hash last element is elmo /

joe
+1  A: 

To make everyone else's point more clear, a hash's keys in Perl will have the same order every time you call keys, values, or each within the same process's lifetime, assuming the hash has not been modified. From perlfunc:

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 perlsec).

Matt Kane
This isn't completely true. The hash keys will only come out in the same order in those cases if you don't change the hash.
brian d foy
Reread the first sentence of my response. The one you edited.
Matt Kane
This also is not always true for tie()'d hashes.
Kevin Panko
+3  A: 

Generally, assuming you want last, sorted alphabetically, it's simple:

use List::Util qw( maxstr );

print maxstr(keys %hash);

If you'd prefer not to use module (which I don't see valid reason for, but there are people who like to make it harder):

print( (sort keys %hash)[-1] );
depesz