* I completely replace the answer so I can build up an example

* The new answer is careful about "sort key" next to "hash key",
which was the problem that motivated the change


Index: perlfaq4.pod
===================================================================
RCS file: /cvs/public/perlfaq/perlfaq4.pod,v
retrieving revision 1.69
diff -u -d -r1.69 perlfaq4.pod
--- perlfaq4.pod  14 Oct 2005 15:34:06 -0000 1.69
+++ perlfaq4.pod  25 Oct 2005 15:40:06 -0000
@@ -1786,27 +1786,50 @@
 
 =head2 How do I sort a hash (optionally by value instead of key)?
 
-Internally, hashes are stored in a way that prevents you from imposing
-an order on key-value pairs.  Instead, you have to sort a list of the
-keys or values:
+(contributed by brian d foy)
 
-    @keys = sort keys %hash; # sorted by key
-    @keys = sort {
-         $hash{$a} cmp $hash{$b}
-      } keys %hash;    # and by value
+To sort a hash, start with the keys. In this example, we give the list
of
+keys to the sort function which then compares them ASCIIbetically
(which
+might be affected by your locale settings). The output list has the
keys
+in ASCIIbetical order. Once we have the keys, we can go through them to
+create a report which lists the keys in ASCIIbetical order.
 
-Here we'll do a reverse numeric sort by value, and if two keys are
-identical, sort by length of key, or if that fails, by straight ASCII
-comparison of the keys (well, possibly modified by your locale--see
-L<perllocale>).
+  my @keys = sort { $a cmp $b } keys %hash;
+  
+  foreach my $key ( @keys )
+     {
+     printf "%-20s %6d\n", $key, $hash{$value};
+     }
 
-    @keys = sort {
-     $hash{$b} <=> $hash{$a}
-          ||
-     length($b) <=> length($a)
-          ||
-           $a cmp $b
-    } keys %hash;
+We could get more fancy in the C<sort()> block though. Instead of 
+comparing the keys, we can compute a value with them and use that
+value as the comparison.  
+
+For instance, to make our report order case-insensitive, we use
+the C<\L> sequence in a double-quoted string to make everything 
+lowercase. The C<sort()> block then compares the lowercased
+values to determine in which order to put the keys.
+
+  my @keys = sort { "\L$a" cmp "\L$b" } keys %hash;
+  
+Note: if the computation is expensive or the hash has many elements,
+you may want to look at the Schwartzian Transform to cache the 
+computation results.
+
+If we want to sort by the hash value instead, we use the hash key
+to look it up. We still get out a list of keys, but this time they
+are ordered by their value.
+
+  my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash;
+
+From there we can get more complex. If the hash values are the same,
+we can provide a secondary sort on the hash key.
+
+  my @keys = sort { 
+     $hash{$a} <=> $hash{$b} 
+        or
+     "\L$a" cmp "\L$b"
+     } keys %hash;
 
 =head2 How can I always keep my hash sorted?

-- 
brian d foy, [EMAIL PROTECTED]

Reply via email to