# Sorting a list by a single particular element in a specified order

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 (#[]), 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[#[], Evaluate[Sequence @@ Flatten[scoreList]]
] &
];


I wish to sort a list of lists by its second element (#[]) 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. 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[#[], 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 {#[] /. Rule @@@ myScoreList &} as the second argument of SortBy:

SortBy[myListToSort1, {#[] /. Rule @@@ myScoreList &}]

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

SortBy[myListToSort2, {#[] /. Rule @@@ myScoreList &}]

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


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, #[] &], 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"}} *)