As I read over Complex hash sorting and considered options for possible solutions I began thinking about how to implement a sort routine that not only can sort on multiple criteria, but that can adapt to the criteria at runtime , thus allowing for dynamic selection of sort criteria.

It seems there are several issues here. First, assume it's unknown at script development time how many search criteria will be used. Second, assume that at script development time it is unknown whether a particular sort criteria will be numeric or string-based. And third, assume that it is unknown at script development time what the order of precedence will be for the various sort criteria.

It struck me that it might be clever to build up a string representation of the multi-criteria sort routine at runtime, when the desired criteria are known. Then one can eval the routine, sort and all, to gain the desired result. Here is what I came up with:

use strict; use warnings; # Name Sex Age IQ Hair Eyes my %hash = ( 'Lisa' => [ 'F', 6, 150, 'Blonde', 'Blue' ], 'Homer' => [ 'M', 40, 105, 'Bald', 'Blue' ], 'Bart' => [ 'M', 9, 120, 'Blonde', 'Brown' ], 'Marge' => [ 'F', 36, 135, 'Blue', 'Blue' ], 'Maggie' => [ 'F', 1, 130, 'Blonde', 'Blue' ] ); # Assume that @criteria is actually obtained at runtime. my @criteria = ( 0, 2, 3 ); my @comparisons; my ( $sample_key, $sample_value ) = each %hash; foreach ( @criteria ) { push @comparisons, "\$hash{\$a}[$_]" . ( $sample_value =~ /^\d+$/ ? " <=> " : " cmp " ) . " \$hash{\$b}[$_]"; } my $routine = join " || ", @comparisons; my @sorted; eval "\@sorted = sort { $routine } keys \%hash" or die "Ick!\n$@\n"; print $_, "\n" foreach @sorted; __OUTPUT__ Maggie Marge Lisa Homer Bart

This method just builds up a sort routine by creating an array of comparison criteria, and joining them together with " || " logical short circuits. The logical short circuit causes the secondary and terciary (etc) criteria to be evaluated if the primary (and so on) criteria evaluate to equality. Using a logical short circuit inside a sort routine is a pretty common idiom. I just worked out one way of allowing the criteria list to be built at runtime.

The resulting sort routine string is interpolated into an eval expression that already contains the necessary skelleton to implement a sort of a hash's keys. What I have intentionally avoided (to reduce complexity) is the necessary logic to allow for dynamic selection of $b<=>$a versus $a<=>$b ordering. It would be possible to allow for that, but to keep this illustration more to the point I glossed over that extra logic.

If anyone else has come up with a more flexible and robust way of creating a sort routine with runtime selected criteria I'd be interested in reading about it here too.


Dave


In reply to Fun with complex sorting on arbitrary criteria list. by davido

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.