This isn't really as hard as it seems. I'll present two versions that return a list of hash keys in sorted order, sorted by whichever of the sub-values you wish. The first method is going to be a straight sort. If efficiency is an issue, it would probably be best to turn this one into a Schwartzian Transform. But that's not absolutely necessary.

Keep in mind that as others have mentioned, turning your hash values into array-refs is probably a better solution, but I'm going to just work with the assumption that you really do want to keep that '||' delimited list as the value to each hash element.

First, the straight sort:

my $criteria = 3; my @sorted = sort { (split /\|\|/, $hash{$a} )[$criteria] cmp (split /\|\|/, $hash{$b} )[$criteria] } keys %hash +;

The method listed above indexes into the split list, using the specified sub-element as the sort criteria.

This next method will allow any arbitrary number of sort criteria, in the order you choose.

my @criteria = (0, 2); my @sorted = map { $_->[0] } sort { $a->[1] cmp $b->[1] } map { [ $_, join( "", ( split /\|\|/, $hash{$_} )[@sorted +] ] } keys %hash;

This second method uses a Schwartzian Transform in conjunction with join to create a single sort criteria comprised of the slice created by taking (possibly multiple) indexes into the split results.

The shortcoming here is that we're doing all string comparison, which sort of wreks havok on strings that have numeric components. But the advantage here is that you can actually specify multiple sort criteria in one search easily.

There are an infinate number of other ways to do this. Here are some thoughts:

You might use eval and build your sort routine dymanically (once) so that it can be of arbitrary complexity, chained together with the logical short-circuit 'or' operator. This will allow the user to specify that he/she needs to sort by the 0th, 2nd, and 1st criteria today, but the 1st and 4th later. Also, by building it up dynamically, you can at runtime decide whether to do a 'cmp' or an '<=>' comparison. I give a simple example of this technique in Fun with complex sorting on arbitrary criteria list.. My example implementation has a few shortcomings too, but most could be overcome without too much extra work.


Dave


In reply to Re: Complex hash sorting by davido
in thread Complex hash sorting by Anonymous Monk

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.