Re: Permanently sort a hash
by ikegami (Patriarch) on Jun 04, 2009 at 14:53 UTC
|
If you don't want to call sort repeatedly, save the result.
my @sorted_keys = sort { $a <=> $b } keys(%foo);
Update: Or if you no longer need the keys:
my @sorted_values = @foo{ sort { $a <=> $b } keys(%foo) };
| [reply] [d/l] [select] |
|
|
ikegami,
While you know and it should be obvious, it may be worth pointing out that any modification to the keys of the hash (inserts or deletes) requires the sorted keys to be re-cached.
| [reply] |
|
|
Yes, but that would also be the case for the ideal solution the OP posted.
| [reply] |
Re: Permanently sort a hash
by kennethk (Abbot) on Jun 04, 2009 at 14:43 UTC
|
| [reply] |
|
|
kennethk,
Actually, that FAQ is wrong.
Although this does keep your hash sorted, you might not like the slow down you suffer from the tie interface.
What Tie::IxHash actually does is preserve your insertion order. It does have rudimentary capability to re-order by asciibetical sorted keys or values but it is not persistent. If you add key/val pairs, they don't insert into their sorted position and you have to re-order them again. You also can't do anything more complex when sorting.
Tie::Hash::Sorted OTOH does answer the FAQ but I feel a bit odd sending a doc patch to p5p pimping my own module.
| [reply] |
|
|
| [reply] |
|
|
ikegami's solution is a good one then - cache the sorted keys. You can even forgo the foreach loop using hash Slices:
my @sorted_keys = sort { $a <=> $b } keys(%foo);
print join("\n",@hash{@sorted_keys})
| [reply] [d/l] |
Re: Permanently sort a hash
by herveus (Prior) on Jun 04, 2009 at 15:01 UTC
|
Howdy!
Why do you feel you need your hashes to behave in a way they weren't meant to?
The short (and flippant) answer to your question is "you can't". Hashes are unordered collections by
their very nature. If you want the keys in a specific order, you have to impose that
order on the list that you get by calling keys(). If the set of keys is
static, you can store the list in an array in any desired order. That array
will keep them in that order always, but now you have to keep the array and the
hash in sync.
You cannot reliably predict the order that keys() will produce for any set of keys
with more than one item. The order will depend on the set of values and the order
in which they were added to the hash. If you examine the source code for your
specific instance of perl, you can examine the hash function. Then you have to
understand how that interacts with keys() to extract the list.
Of course, there are modules that will let you impose an ordering on keys(), but
they have to subvert the standard behavior of hashes.
This question is analogous to asking for the rows in a table in a database to be
kept "sorted". All that really means is "I don't want to have to sort the data
myself each time I use it". One of the "costs" of using a hash is that the data
is inherently unordered. If you must have it in a specific order, you either have
to sort it into that order each time, or you have to use an array to maintain
the desired order. Or you have to put an ORDER BY clause on your SELECT statement.
| [reply] |
Re: Permanently sort a hash
by Arunbear (Prior) on Jun 04, 2009 at 15:54 UTC
|
use strict;
use warnings;
use DB_File;
tie my %foo, "DB_File", undef, undef, undef, $DB_BTREE;
%foo = (1 => 'value1',2 => 'value2',3 => 'value3');
while(my($k, $v) = each %foo) {
print "$k -> $v\n";
}
| [reply] [d/l] |
|
|
Interesting, but it might not apply here. It returns the keys in lexical order (1,10,2,3,4,5,6,7,8,9) while the OP is sorting the keys in numerical order (1,2,3,4,5,6,7,8,9,10).
| [reply] |
|
|
The default sort is lexical, but it can be made numeric:
use strict;
use warnings;
use DB_File;
$DB_BTREE->{'compare'} = sub { $_[0] <=> $_[1] };
tie my %foo, "DB_File", undef, undef, undef, $DB_BTREE;
for (1 .. 10) {
$foo{$_} = 'value' . $_;
}
while(my($k, $v) = each %foo) {
print "$k -> $v\n";
}
| [reply] [d/l] |
|
|
Re: Permanently sort a hash
by jettero (Monsignor) on Jun 04, 2009 at 15:03 UTC
|
| [reply] |
|
|
jettero,
Yes, but while that module provides rudimentary sorting capability - it is primarily designed to preserve insertion order. I wrote Tie::Hash::Sorted for far more complex sorting needs as well as a number of optimizations under the covers.
| [reply] |
Re: Permanently sort a hash
by Anonymous Monk on Jun 04, 2009 at 15:30 UTC
|
Thank you everyone for your replies. Appreciate your help a lot, especially herveus's explanation!
I will give a try to Tie::Hash::Sorted although I think ikegami's solution is the simplest in my case.
| [reply] |