Message Boards Message Boards

1
|
3818 Views
|
9 Replies
|
10 Total Likes
View groups...
Share
Share this post:

Help with multiple sort of lists

Posted 11 years ago
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?
POSTED BY: Paul Cleary
9 Replies
Posted 11 years ago
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. 
POSTED BY: Paul Cleary
-"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.
POSTED BY: Sander Huisman
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
POSTED BY: Sander Huisman
@Sander, this is very neat.
@Paul, The documentation for this trick is here ( Details -> Item 3) . My code basically shows what it means. 
POSTED BY: Shenghui Yang
Thanks Shenghui Yang! This one saved me already many many times ;-)
POSTED BY: Sander Huisman
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?
POSTED BY: Udo Krause
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[x_] 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.
POSTED BY: Sander Huisman
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. 
POSTED BY: Udo Krause
@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]]
POSTED BY: Shenghui Yang
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract