Assuming you want counts in descending order and titles ascending:
print map join(" ", @$_{qw/ count title /}) . "\n",
sort { $b->{count} <=> $a->{count}
||
$a->{title} cmp $b->{title} }
@$pets;
That's compact code written in a functional style. To help understand it, let's look at equivalent code in a more familiar, imperative style.
Perl's sort
operator takes an optional SUBNAME parameter that allows you to factor out your comparison and give it a name that describes what it does. When I do this, I like to begin the sub's name with by_
to make sort by_...
ready more naturally.
To start, you might have written
sub by_count_or_title {
$b->{count} <=> $a->{count}
||
$a->{title} cmp $b->{title}
}
my @sorted = sort by_count_or_title @$pets;
Note that no comma follows the SUBNAME in this form!
To address another commenter's question, you could use or
rather than ||
in by_count_or_title
if you find it more readable. Both <=>
and cmp
have higher precedence (which you might think of as binding more tightly) than ||
and or
, so it's strictly a matter of style.
To print the sorted array, a more familiar choice might be
foreach my $p (@sorted) {
print "$p->{count} $p->{title}\n";
}
Perl uses $_
if you don't specify the variable that gets each value, so the following has the same meaning:
for (@sorted) {
print "$_->{count} $_->{title}\n";
}
The for
and foreach
keywords are synonyms, but I find that the uses above, i.e., foreach
if I'm going to name a variable or for
otherwise, read most naturally.
Using map
, a close cousin of foreach
, instead isn't much different:
map print("$_->{count} $_->{title}\n"), @sorted;
You could also promote print
through the map
:
print map "$_->{count} $_->{title}\n",
@sorted;
Finally, to avoid repetition of $_->{...}
, the hash slice @$_{"count", "title"}
gives us the values associated with count and title in the loop's current record. Having the values, we need to join them with a single space and append a newline to the result, so
print map join(" ", @$_{qw/ count title /}) . "\n",
@sorted;
Remember that qw//
is shorthand for writing a list of strings. As this example shows, read a map
expression back-to-front (or bottom-to-top the way I indented it): first sort the records, then format them, then print them.
For one final alternative, you could eliminate the temporary @sorted
but call the named comparison:
print map join(" ", @$_{qw/ count title /}) . "\n",
sort by_count_or_title
@$pets;