# Ordering a list of rules by its 4 last elements of LHS

I have a list of rules such as

listofrules = {{1, 1, 2, 5, 14} -> 1, {1, 1, 2, 7, 12} -> 1, {137, 1, 2, 6, 16} ->
1, {137, 1, 2, 8, 13} -> 1, {273, 1, 2, 3, 5} ->
1, {273, 1, 2, 3, 7} -> 2, {273, 1, 2, 9, 12} ->
1, {273, 1, 2, 9, 14} -> -2, {409, 1, 2, 3, 6} ->
1, {409, 1, 2, 3, 8} ->
2, {409, 1, 2, 9, 13} -> -1, {409, 1, 2, 9, 16} ->
2, {545, 1, 2, 4, 5} -> 1, {577,1,1,1,1} -> -3 ...... , {9520, 16, 16, 13, 16} -> 2, {9520, 16, 16, 1, 1} ->
2, {9520, 16, 16, 2, 2} -> -2, {9520, 16, 16, 5, 5} -> -4, {9520,
16, 16, 6, 6} ->
3, {9520, 16, 16, 7, 7} -> -12, {9520, 16, 16, 8, 8} -> -1, {9520,
16, 16, 10, 10} -> -1, {9520, 16, 16, 12, 12} -> -4, {9520, 16, 16,
13, 13} ->
3, {9520, 16, 16, 14, 14} -> -12, {9520, 16, 16, 15, 15} ->
4, {9520, 16, 16, 16, 16} -> -1}


I want to sort it, ordering the last four elements of the LHS in growing order, irrespective of the first element.

For example, I want all the rules {#,1,1,1,1} -> # first, then all the rules {#,1,1,1,2} -> # etc...

How can I do that in an efficient way?

Try

listofrules={
{1,1,2,5,14}->1,{1,1,2,7,12}->1,{137,1,2,6,16}->1,{137,1,2,8,13}->1,
{273,1,2,3,5}->1,{273,1,2,3,7}->2,{273,1,2,9,12}->1,{273,1,2,9,14}->-2,
{409,1,2,3,6}->1,{409,1,2,3,8}->2,{409,1,2,9,13}->-1,{409,1,2,9,16}->2,
{545,1,2,4,5}->1,{577,1,1,1,1}->-3,{9520,16,16,13,16}->2,{9520,16,16,1,1}->2,
{9520,16,16,2,2}->-2,{9520,16,16,5,5}->-4,{9520,16,16,6,6}->3,
{9520,16,16,7,7}->-12,{9520,16,16,8,8}->-1,{9520,16,16,10,10}->-1,
{9520,16,16,12,12}->-4,{9520,16,16,13,13}->3,{9520,16,16,14,14}->-12,
{9520,16,16,15,15}->4,{9520,16,16,16,16}->-1};
SortBy[listofrules,#[[1,{2,3,4,5}]]&]


which extracts the items that you want to sort by and thus returns

{{577, 1, 1, 1, 1} -> -3, {273, 1, 2, 3, 5} -> 1, {409, 1, 2, 3, 6} -> 1,
{273, 1, 2, 3, 7} -> 2, {409, 1, 2, 3, 8} -> 2, {545, 1, 2, 4, 5} -> 1,
{1, 1, 2, 5, 14} -> 1, {137, 1, 2, 6, 16} -> 1, {1, 1, 2, 7, 12} -> 1,
{137, 1, 2, 8, 13} -> 1, {273, 1, 2, 9, 12} -> 1, {409, 1, 2, 9, 13} -> -1,
{273, 1, 2, 9, 14} -> -2, {409, 1, 2, 9, 16} -> 2, {9520, 16, 16, 1, 1} -> 2,
{9520, 16, 16, 2, 2} -> -2, {9520, 16, 16, 5, 5} -> -4, {9520, 16, 16, 6, 6} -> 3,
{9520, 16, 16, 7, 7} -> -12, {9520, 16, 16, 8, 8} -> -1,
{9520, 16, 16, 10, 10} -> -1, {9520, 16, 16, 12, 12} -> -4,
{9520, 16, 16, 13, 13} -> 3, {9520, 16, 16, 13, 16} -> 2,
{9520, 16, 16, 14, 14} -> -12, {9520, 16, 16, 15, 15} -> 4,
{9520, 16, 16, 16, 16} -> -1}


For this particular choice of the items to sort by

SortBy[listofrules,Rest[#[[1]]]&]


will also work. And there are probably several other ways of selecting the desired four elements.

• Thank you, it works! Can you explain me what #[[1,{2,3,4,5}]]&] does, please? Aug 4, 2020 at 8:39
• Sure. Part of the culture of Mathematica is to turn lots of stuff into punctuation characters. Stuff followed by & is a function, but we just use it and don't give it any name, look at the documentation reference.wolfram.com/language/ref/Function.html This function is going to have a single argument and that is indicated by # which will be any one of the items in your list. Then #[[1]] will extract the first part of your rule. And #[[1,{2,3,4,5}]] is going to extract the 2nd through 5th items out of that first part. SortBy uses this to extract the items to sort by. Is that enough?
– Bill
Aug 4, 2020 at 8:53
• Perhaps compare this f[r_]:=r[[1,{2,3,4,5}]]; SortBy[listofrules,f] to the #& version and see if that helps you a little. You can also search the help system for things like [[ and that will provide some explanation on how this is extracting the desired bits.
– Bill
Aug 4, 2020 at 17:08
#[[Ordering[#[[All, 1, 2 ;;]]]]] & @ listofrules

{{577, 1, 1, 1, 1} -> -3, {273, 1, 2, 3, 5} -> 1, {409, 1, 2, 3, 6} -> 1,
{273, 1, 2, 3, 7} ->  2, {409, 1, 2, 3, 8} -> 2, {545, 1, 2, 4, 5} -> 1,
{1, 1, 2, 5, 14} -> 1, {137, 1, 2, 6, 16} -> 1, {1, 1, 2, 7, 12} -> 1,
{137, 1, 2, 8, 13} -> 1, {273, 1, 2, 9, 12} -> 1, {409, 1, 2, 9, 13} -> -1,
{273, 1, 2, 9, 14} -> -2, {409, 1, 2, 9, 16} -> 2, {9520, 16, 16, 1, 1} -> 2,
{9520, 16, 16, 2, 2} -> -2, {9520, 16, 16, 5, 5} -> -4,
{9520, 16, 16, 6, 6} -> 3, {9520, 16, 16, 7, 7} -> -12, {9520, 16, 16, 8, 8} -> -1,
{9520, 16, 16, 10, 10} -> -1, {9520, 16, 16, 12, 12} -> -4,
{9520, 16, 16, 13, 13} -> 3, {9520, 16, 16, 13, 16} -> 2,
{9520, 16, 16, 14, 14} -> -12, {9520, 16, 16, 15, 15} -> 4,
{9520, 16, 16, 16, 16} -> -1}


This should be faster than SortBy for long lists.

• How come two # and one &? How should I think about that? Aug 4, 2020 at 15:33
• @PaulCommentary, alternatively, we can define the function #[[Ordering[#[[All, 1, 2 ;;]]]]] &  as foo[x_] := x[[Ordering[x[[All, 1, 2 ;;]]]]] and use it as foo[listofrules].
– kglr
Aug 4, 2020 at 15:42

Another possibility using operator forms (and avoiding pure functions) is:

SortBy[listofrules, Rest @* First]


{{577, 1, 1, 1, 1} -> -3, {273, 1, 2, 3, 5} -> 1, {409, 1, 2, 3, 6} -> 1, {273, 1, 2, 3, 7} -> 2, {409, 1, 2, 3, 8} -> 2, {545, 1, 2, 4, 5} -> 1, {1, 1, 2, 5, 14} -> 1, {137, 1, 2, 6, 16} -> 1, {1, 1, 2, 7, 12} -> 1, {137, 1, 2, 8, 13} -> 1, {273, 1, 2, 9, 12} -> 1, {409, 1, 2, 9, 13} -> -1, {273, 1, 2, 9, 14} -> -2, {409, 1, 2, 9, 16} -> 2, {9520, 16, 16, 1, 1} -> 2, {9520, 16, 16, 2, 2} -> -2, {9520, 16, 16, 5, 5} -> -4, {9520, 16, 16, 6, 6} -> 3, {9520, 16, 16, 7, 7} -> -12, {9520, 16, 16, 8, 8} -> -1, {9520, 16, 16, 10, 10} -> -1, {9520, 16, 16, 12, 12} -> -4, {9520, 16, 16, 13, 13} -> 3, {9520, 16, 16, 13, 16} -> 2, {9520, 16, 16, 14, 14} -> -12, {9520, 16, 16, 15, 15} -> 4, {9520, 16, 16, 16, 16} -> -1}