All Sorts of Sorts: Hashes
February 7, 2000
We said earlier that hashes are not ordered, which would
seem to imply that you can't sort a hash. That's true. But
we might wish to sort the keys and/or values within a hash,
which can be done when thinking of the keys or values
as lists unto themselves. Some demonstration will probably
be more useful than explanation.
A fictional prices hash contains keys which are
product names and values which are product prices.
%prices=("deluxe gizmo"=>24.99,
"ultimate gizmo"=>49.99,
"standard gizmo"=>19.99,
"ultra gizmo"=>31.99);
Suppose we want to output the products and prices in this
hash, sorted in order from least expensive to most
expensive? As stated, we can't control the internal order
of the hash, but we don't need to. Rather, using the
keys function we can pull out the hash keys as
a list, sort these keys based on their hash values, and
output the results.
foreach $item (sort {$prices{$a}<=>$prices{$b} }
keys %prices)
{print "$item: \$$prices{$item}\n"};
Yields:
standard gizmo: $19.99
deluxe gizmo: $24.99
ultra gizmo: $31.99
ultimate gizmo: $49.99
The foreach statement here prepares to iterate over
a list. What list? The function keys (%prices)
return a list of all keys in the hash prices, which
consist of "deluxe gizmo", "standard gizmo",
and so forth. This list is sorted by the presence of the
sort function, using the sort code that we provide.
Our sort code uses the hash to look up the value (price)
of key $a compared to the price of key $b. As a result, the
key names are sorted by virtue of their associated prices.
The print statement formats the results by printing
each key followed by its associated value from the
prices hash.
Another spin on this theme is to use a hash to sort a list
by frequency. For instance, suppose we have a long list of
last names, and we want to sort this list by frequency of
instances of each name. In fact, this is rather easy thanks
to hashes -- plus, this technique offers the added benefit
of removing duplicates from a list, as well. Imagine our
list of surnames:
@surnames=("jones","harris","stein",
"harris","norman","jones","harris");
We can setup an empty hash, then iterate through
surnames and add each as a key of that hash.
%surname_freq=();
foreach $surname (@surnames)
{$surname_freq{$surname}++}
The first name on the list is "jones", so
"jones" becomes a key in the hash surname_freq.
The value for this hash is set by the increment operator,
which simply increments the value by one. Since this hash
key began with no value, or zero, its value becomes one. The
same procedure occurs for each name in the list. When done,
we'll have a hash with a key "harris" whose value
will be 3. Neat! We can output the sorted hash using the
same technique seen with the gizmos, thereby producing a
sorted descending frequency table and a list with no
duplicates:
foreach $surname
(reverse sort
{$surname_freq{$a}<=>$surname_freq{$b} } keys %surname_freq)
{print "$surname: frequency $surname_freq{$surname}\n";}
Keeping track of the braces becomes crucial in this code.
Here, we pull out the list of surname keys and reverse sort
them by their associated frequencies, finally printing the
results.
All Sorts of Sorts: Lists
The Perl You Need to Know
A List of Lists
|