forrest has asked for the wisdom of the Perl Monks concerning the following question:
My sort requirements are: If the title is 'THIS BOOK FIRST', that needs to go first, followed by any other books by the same author alphabetically by title. All other books follow alphabetically by author, then title. Is it possible to write a sort routine so that@things_to_sort = ( { author => 'bart', title => 'skateboarding' }, { author => 'lisa', title => 'postmodernism' }, { author => 'marge', title => 'hairstyles' }, { author => 'lisa', title => 'THIS BOOK FIRST' }, { author => 'homer', title => 'donuts' }, { author => 'bart', title => 'coolness' } );
will do what I need? Or do I need to break it up into @things_at_the_top and @the_rest, sort those and then smack the list back together? I think I can do that, but it's hella ugly. Can anyone suggest an elegant solution to this problem?@sorted_things = sort by_twisted_requirements @things_to_sort
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Twisted sort requirements
by BrowserUk (Patriarch) on Feb 01, 2006 at 07:42 UTC | |
You are going to need a pre-pass of the data to find the author of the titled book. I've lower cased the titles for the sort so that "the book of ... " and "The book of..." will sort together, but that is easily removed. This assumes that your authors names and book titles don't contain null chars.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
Re: Twisted sort requirements
by davido (Cardinal) on Feb 01, 2006 at 07:08 UTC | |
This should do it:
This uses logical short-circuiting to "fall through" until one of the comparisons produces a result. First, $a is tested to see if it has the key phrase. If not, then $b is tested. If not, then $a and $b are compared to see if there is inequality in author names. If so, great, but if not, fall through to the title comparison. Dave | [reply] [d/l] |
by forrest (Beadle) on Feb 01, 2006 at 07:25 UTC | |
I guess that might not be possible with a simple sort call. | [reply] [d/l] |
by davido (Cardinal) on Feb 01, 2006 at 07:35 UTC | |
It is definately possible. graff kindly /msg'ed me to let me know I missed a specification. Update: I have now re-written my original method in such a way that the author is stored for future comparisons if title is "THIS BOOK FIRST". I then rewrote the logic into basic 'if' conditionals so that I could visualize the flow a little better. But I guess I've stared at it too long, and am simply missing something that someone's going to spot quickly. Unfortunately, that "something" is eluding me. Here is my code so far, that doesn't result in the correct sort order. I hope someone spots my flaw because it's going to keep me awake tonight.
I know it's really ugly. The "if" statements tend to do that, but I converted over to "if"s to help in visualizing the flow. ...obviously it didn't help me see the problem. I can't wait to hear if anyone else can spot it. Dave | [reply] [d/l] |
by davido (Cardinal) on Feb 01, 2006 at 08:31 UTC | |
by graff (Chancellor) on Feb 01, 2006 at 08:41 UTC | |
|
Re: Twisted sort requirements
by GrandFather (Saint) on Feb 01, 2006 at 07:49 UTC | |
With a little work before hand sort is the tool to use:
Prints:
DWIM is Perl's answer to Gödel | [reply] [d/l] [select] |
|
Re: Twisted sort requirements
by graff (Chancellor) on Feb 01, 2006 at 07:37 UTC | |
I couldn't figure out a way to do it all in a single sort block -- especially when it comes to figuring out which author should be listed first. That just seemed to need an extra step at the outset. Maybe there's a way to use just one sort block after grepping for "THIS BOOK FIRST", but I haven't found it... update: my first posting had a much more complicated block for the first sort -- which was a failed attempt to do everything in one sort -- but I simplified it to just the appropriate logic. | [reply] [d/l] |
|
Re: Twisted sort requirements
by jonadab (Parson) on Feb 01, 2006 at 13:29 UTC | |
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.)
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. | [reply] [d/l] |
|
Re: Twisted sort requirements
by salva (Canon) on Feb 01, 2006 at 14:06 UTC | |
You are storing in the same array two kinds of unrelated information: a list of books and author ordering information. Using a magic marker is a bad idea almost every time. In that case, the obvious data structures to use are an array to store book descriptions and a hash to store author ordering information. If you are getting the information from a DB, the information should already be structured that way there. You should even be able to get the list of books ordered from the database! If you are reading the information from a file (or from the network) you can build those two structures on the fly on the parsing phase with almost identical effort and then other parts of your code will become much cleaner. | [reply] |
|
Re: Twisted sort requirements
by injunjoel (Priest) on Feb 01, 2006 at 17:58 UTC | |
A little late I see but here is what I would do... The Output
-InjunJoel P.S. thanks davido for the sort idea.
"I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
| [reply] [d/l] [select] |
|
Re: Twisted sort requirements
by NetWallah (Canon) on Feb 02, 2006 at 01:28 UTC | |
I liked injunjoel's streightforward code, but I wanted to simplify it, and make special cases stand out. I also dislike handling special cases inside sort routines, because these are called more often than the number of items to be sorted. I also believe I have minimized the number of loops. The only part I am not happy with is the "Special Marker" that is passed into the sort. That is a compromise made in order to simplify the code: I added an extra data line, to ensure that sorting was alpha, according the OP's spec. The output: Extending this to allow Multiple "First Authors" is left as an exercise for the reader. "For every complex problem, there is a simple answer ... and it is wrong." --H.L. Mencken | [reply] [d/l] [select] |
by johngg (Canon) on Feb 02, 2006 at 15:01 UTC | |
I tried to think of a way to accomplish the first pass in the transform to make things simpler but failed. Cheers, JohnGG | [reply] [d/l] |