You can get most of what you need with a Schwartzian Transform, but there's one little wrinkle that complicates things: during the first map, when you run across an arbitrary title (say, 'postmodernism' by 'lisa'), you don't yet know whether the same author also has a 'THIS BOOK FIRST' title. So you need an extra pass, and an orcish manoeuver, to tell you that.

(If terms like 'Schwartzian Transform' and 'orcish manoeuver' are new for you, you should first look those up (Google is good here) and try to understand them before trying to use the code below.)

my %orcish; my @sorted = map { # Final pass: restore the original datum, dropping # the extra stuff we added in the second pass: $$_[4] } sort { # Books whose author has a 'THIS BOOK FIRST' title are first: $$a[0] <=> $$b[0] # Within that, sort by author: or $$a[1] <=> $$b[1] # Within that, sort by whether it's THIS BOOK FIRST or not: or $$a[2] <=> $$b[2] # Within that, sort by the title, alphabetically: or $$a[3] <=> $$b[3] } map { # Second pass: gather enough info that sort can sort like we want: [$orcish{$$_{author}}, $$_{author}, ($$_{title} eq 'THIS BOOK FIRST'), $$_{title}, $_] } map { # First pass: take note of which authors have 'THIS BOOK FIRST': $orcish{$$_{author}}++ if $$_{title} eq 'THIS BOOK FIRST'; # But just pass through the data unmodified for now: $_ } @things_to_sort;

Note that the way I've written this, if an author has _two_ books with the magic title, that author's works will be listed before the ones who only have _one_ such title. If this is undesireable, change the ++ to =1 in the first pass.

Also note that with this structure in place it's easy to now make further refinements to the sort order, by making slight modifications to the second pass and/or the sort. For instance, if I wanted the words "A", "An", and "The" on the beginnings of titles to be ignored for sorting purposes, then in the second pass, instead of using $$_{title} directly, I would use title_for_sort_purposes($$_{title}) and then write that subroutine to strip initial articles from its input and return what is left. Case unification could also be performed, whitespace consolidation, and whatever other forms of normalization you desire. The author's name, too, can be handled in this way, and in fact could be parsed and the last name split out (if your author names aren't already in "last, first" form), and so forth. The original data are retained as the last element in each anonymous array, so you can go nuts making adjustments to the other elements to tweak how they sort, without changing how they appear once sorted.


In reply to Re: Twisted sort requirements by jonadab
in thread Twisted sort requirements by forrest

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.