6
$\begingroup$

I have a list of lists -- a list of sublists -- that I wish to sort in a specified order. The following function, sortBy2ndElement, sorts a list of lists (listToSort) by the second element of each sublist, in the order specified by a score list (scoreList).

(* Function 'sortBy2ndElement': Sort the list 'listToSort' by the
second element (#[[2]]), in the order specified by the list 'scoreList'.

'scoreList' is of the form {{"C",1},{"A",2},{"B",3}} if the list
'listToSort' is to be sorted by the second element in the order "C", "A", "B". *)

sortBy2ndElement[listToSort_List, scoreList_List] := SortBy[listToSort,
   Switch[#[[2]], Evaluate[Sequence @@ Flatten[scoreList]]
     ] &
   ];

I wish to sort a list of lists by its second element (#[[2]]) in the order "C", "A", "B", so my score list is as follows:

myScoreList = {{"C", 1}, {"A", 2}, {"B", 3}};

Simple example (output is acceptable)

Now try sorting a test list, myListToSort1:

myListToSort1 = {
   {"Y", "A", "R2"},
   {"X", "B", "S2"},
   {"Z", "C", "T2"}
   };
sortBy2ndElement[myListToSort1, myScoreList]

{{"Z", "C", "T2"}, {"Y", "A", "R2"}, {"X", "B", "S2"}}

So myListToSort1 is sorted as desired.

More complicated example (output is unacceptable)

But now suppose the list has multiple sublists containing each of the elements in the score list. For example:

myListToSort2 = {
   {"Y", "A", "R2"},(* I wish to keep the third elements in the order given. *)
   {"Y", "A", "R1"},
   
   {"X", "B", "S2"},
   {"X", "B", "S1"},
   
   {"Z", "C", "T2"},
   {"Z", "C", "T1"}
   };

When I operate sortBy2ndElement on myListToSort2, I obtain the following:

{{"Z", "C", "T1"}, {"Z", "C", "T2"}, {"Y", "A", "R1"}, {"Y", "A", "R2"}, {"X", "B", "S1"}, {"X", "B", "S2"}}

The function has sorted the second-element sublists in the order "C", "A", "B" as desired, but it has also sorted the third-element sublists into canonical order (i.e., "T1", "T2"; "R1", "R2"; and "S1", "S2").

My question

I would like to modify the function sortBy2ndElement so that it retains the ordering of the third-element sublists given in the input. So, instead of the above, I wish to obtain this output:

{"Z", "C", "T2"}, {{"Z", "C", "T1"}, {"Y", "A", "R2"}, {"Y", "A", "R1"}, {"X", "B", "S2"}, {"X", "B", "S1"}}

How do I do this? It seems the problem is with SortBy sorting in canonical order.

Please know that I'm running a very old version of Mathematica, version 9.0. If possible, I would like potential solutions to work on Mathematica 9.0.

$\endgroup$

2 Answers 2

8
$\begingroup$

Sort:

enter image description here

1. For a minimal change in OP's function, wrap the scoring function in a list, i.e., use {Switch[...]&}, in the second argument of SortBy:

sortBy2ndElementB[listToSort_List, scoreList_List] := 
 SortBy[listToSort, {Switch[#[[2]], Evaluate[Sequence @@ Flatten[scoreList]]] &}]

sortBy2ndElementB[myListToSort1, myScoreList]
{{"Z", "C", "T2"}, {"Y", "A", "R2"}, {"X", "B", "S2"}}
sortBy2ndElementB[myListToSort2, myScoreList]
{{"Z", "C", "T2"}, {"Z", "C", "T1"}, {"Y", "A", "R2"}, {"Y", "A", "R1"},  
 {"X", "B", "S2"}, {"X", "B", "S1"}}

2. Alternatively, use {#[[2]] /. Rule @@@ myScoreList &} as the second argument of SortBy:

SortBy[myListToSort1, {#[[2]] /. Rule @@@ myScoreList &}]
{{"Z", "C", "T2"}, {"Y", "A", "R2"}, {"X", "B", "S2"}}
SortBy[myListToSort2, {#[[2]] /. Rule @@@ myScoreList &}]
{{"Z", "C", "T2"}, {"Z", "C", "T1"}, {"Y", "A", "R2"}, {"Y", "A","R1"}, 
{"X", "B", "S2"}, {"X", "B", "S1"}}
$\endgroup$
5
$\begingroup$

Gather elements with the same second element first, then sort, then Flatten. A minimal change to your code would be:

myListToSort2 = {{"Y", "A", "R2"}, {"Y", "A", "R1"}, {"X", "B", "S2"}, {"X", "B", "S1"}, {"Z", "C", "T2"}, {"Z", "C", "T1"}};
myScoreList = {{"C", 1}, {"A", 2}, {"B", 3}};

SortBy[GatherBy[myListToSort2, #[[2]] &], Switch[#[[1, 2]], Evaluate[Sequence @@ Flatten[myScoreList]]] &]~Flatten~1
(* {{"Z", "C", "T2"}, {"Z", "C", "T1"}, {"Y", "A", "R2"}, {"Y", "A", "R1"}, {"X", "B", "S2"}, {"X", "B", "S1"}} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.