Help with multiple sort of lists

I have an {n x 3} list and wish to sort first by column 3 and then by column 2 and still maintain the first sort when I do the second sort. For example I have this list.

l = {{b, 98, 999}, {c, 75, 999}, {a, 100, 999}, {d, 67, 987}, {e, 98, 987}}

and using this sort does not do what I need it to do.

Sort[l,#1[[3]]>#2[[3]]&&#1[[2]]>#2[[2]]&];
Column[l]

produces this output

{b,98,999}
{c,75,999}
{a,100,999}
{d,67,987}
{e,98,987}

I am trying to get this result

{a,100,999}
{b,98,999}
{c,75,999}
{e,98,987}
{d,67,987}

I have tried SortBy and cant get any closer, I have re-organised the rows so the numbers are first and still no success. Is this possible using the Sort command or will I have to resort to a more programmable way?

@Paul, this is usually done by mixing the Sort function and the GatherBy function. The following code generates the result in the form that you have requested:


When you try to do the secondary sort, you basically just want to operate on the subset of the data sharing the same third element. GatherBy gives you a nice and neat way to collect such subsets. Also in this code “%” denotes the result from the last step and “/@” means Map function. The copyable code is here:

l = {{b, 98, 999}, {c, 75, 999}, {a, 100, 999}, {d, 67, 987}, {e, 98, 987}};
Sort[l, #1[[3]] > #2[[3]] &];
GatherBy[l, #[[3]] &];
Sort[#, #1[[2]] > #2[[2]] &] & /@ %;
TableForm[Flatten[%, 1]]

This can be easily solved with SortBy, and using multiple functions to sort out ‘ties’.

l={{b,98,999},{c,75,999},{a,100,999},{d,67,987},{e,98,987}};
SortBy[l,{-#[[3]]&,-#[[2]]&}];
%//Grid

out:

a	100	999
b	98	999
c	75	999
e	98	987
d	67	987

@Sander, this is very neat.
@Paul, The documentation for this trick is here ( Details → Item 3) . My code basically shows what it means.

Thanks Shenghui Yang! This one saved me already many many times :wink:

Hi Sander,

Question: What do you think is reason the following fails:

In[1]:= Clear[l]
l = {{b, 98, 999}, {c, 75, 999}, {a, 100, 999}, {d, 67, 987}, {e, 98, 
    987}};

In[3]:= Clear[clearySort]
clearySort[x_, y_] := If[x[[-1]] > y[[-1]], True,
  If[x[[-2]] > y[[-2]], True, False]
  ]

In[5]:= SortBy[l, clearySort]

Out[5]= {{a, 100, 999}, {b, 98, 999}, {c, 75, 999}, {d, 67, 987}, {e, 98, 987}}

it’s wrong in the last two elements and clearySort knows about that:

In[6]:= clearySort[{d, 67, 987}, {e, 98, 987}]

Out[6]= False

Wasn’t it the meaning that sort stops only if all distinct pairs say “True” (or at least “Don’t know”) under the criterion?

SortBy does not work on pairs, but on individual elements only:

SortBy[f,expr] will evaluate:
g=f[expr[[1]]], f[expr[[2]]], f[expr[[2]]]… (or g=f/@exp for short)
f is then return in the order given by Ordering.

So in your example clearysort is never executed because clearlysort is not defined. It will be left unevaluated:

g={clearysort[{b,98,999}], ... ,clearysort[{e,98,987}]}

If we sort g it will just ignore the heads because they are all the same, and that is why you get a list back in the form of a…, b… , c…, d…, e…
FYI: your code does work if you use Sort instead of SortBy.
Hope this helps.

You are right

In[3]:= SortBy[l, True]
Out[3]= {{a, 100, 999}, {b, 98, 999}, {c, 75, 999}, {d, 67, 987}, {e, 98, 987}}

the possibility of simply being ignored had been ignored, thanks.

Sorry about my late reply, and thank you Shenghui and Sander for your replies. The SortBy method suggested by Sander is an excellent method, I have now extended my sorts to 3 and 4 columns. One thing worthy of note, I have found sorting a text field does not respond to -#[[4]]. it sorts but alphabetically and can’t reverse the sort. I don’t find this much of a problem though. Thanks again for your help.Paul.

-“string” does not make much sense, for numbers it does: it will maken 90 and 100 (increasing order) change to -90 and -100 (decreasing order)
you have to make a special function to deal with reverse strings: you can change all charachters:
a.b.c.d…z → z.y.x.w…a
and use that to sort it. Then you can achieve reverse ordering with strings.