Sorting a list with secondary criterion

Is there an easy way to sort a list on multiple levels of criteria?

By this I mean, first the list should be sorted according by criteria A (the same as using the usual sort function, Sort[list, A[#1] < A[#2] & ]. I am a big fan of using the pure ordering function in Sort.). However, then I want to have elements with the same value for criterion A to be sorted within their class by criterion B. In general, I would like to do this to an arbitrary depth of criteria.

An example would be, given a set of colored blocks, first sort the blocks alphabetically by shape, then (maintaining that all the squares come before all the triangles) sort each cluster of shapes alphabetically by their colors. (Blue square, Purple square, Red square, black triangle, orange triangle, yellow triangle, etc.)

-

This is implemented in SortBy:

Because this function does not perform a pairwise compare, you would need to be able to recast your sort function to produce a canonical ordering. On the upside, if you are able to do so it will be far more efficient than Sort.

f1 = Mod[#, 4] &;
f2 = Mod[#, 7] &;

SortBy[Range@10, {f1, f2}]

{#, f1@#, f2@#} & /@ % // Grid

{8, 4, 1, 9, 5, 2, 10, 6, 7, 3}


$\begin{array}{r} 8 & 0 & 1 \\ 4 & 0 & 4 \\ 1 & 1 & 1 \\ 9 & 1 & 2 \\ 5 & 1 & 5 \\ 2 & 2 & 2 \\ 10 & 2 & 3 \\ 6 & 2 & 6 \\ 7 & 3 & 0 \\ 3 & 3 & 3 \end{array}$

Also see:

-
Excellent! Thank you. I knew there had to be some simple way of doing this. – Alexander Gruber Aug 25 '12 at 3:16

Coming from a long experience in Perl, one of my great disappointments in Mathematica was the tricky business of sorting multidimensional lists and strings.

Sort orders strings as in a dictionary, with uppercase versions of letters coming after lowercase ones. Your example calls for ASCII sorting. Unfortionately there is no "ASCII" option in Sort nor SortBy.

Standard sorting strings of different lengths gives:

string = {"cat", "fish", "catfish", "Cat"};
Sort[string]
out: {"cat", "Cat", "catfish", "fish"}


In order to get ASCII sorting one has to dissect the strings into their ASCII codes:

ToCharacterCode[string]
out: {{99, 97, 116}, {102, 105, 115, 104}, {99, 97, 116, 102, 105, 115,104}, {67, 97, 116}}


This can be sorted by SortBy[...,First], where ties are now solved by the next value in each of the sublists:

SortBy[ToCharacterCode[string], First]
out: {{67, 97, 116}, {99, 97, 116}, {99, 97, 116, 102, 105, 115, 104}, {102, 105, 115, 104}}


The ASCII-betically sorted list:

FromCharacterCode[
SortBy[ToCharacterCode[string], First]
]
out: {"Cat", "cat", "catfish", "fish"}

-
"ASCII-betical" sorting is more compactly done as SortBy[{"cat", "fish", "catfish", "Cat"}, Composition[First, ToCharacterCode]]. – J. M. Aug 25 '12 at 11:13
Thank you, very smart. – Romke Bontekoe Aug 25 '12 at 11:41
@J.M. Here, I discussed the generalization of your idea. – Leonid Shifrin Aug 25 '12 at 17:41
If you really want an ASCII sort of a list of strings I recommend: #[[Ordering @ PadRight @ ToCharacterCode @ #]] & – Mr.Wizard Aug 25 '12 at 20:20
@Mr.Wizard, very direct solution. Thanks too. – Romke Bontekoe Aug 27 '12 at 14:27