6
$\begingroup$

I'm having a bit of trouble figuring out the right Sort syntax to use on a list of lists. Consider the following list:

list=
{
 {"B0T", "Ch72", "T0p36K", "Vdiv2047p92", "Vg75V"},
 {"B0T", "Ch70", "T0p28K", "Vdiv2047p92", "Vg65V"},
 {"B0T", "Ch70", "T0p28K", "Vdiv2047p92", "Vg60V"}, 
 {"B0T", "Ch72", "T0p36K", "Vdiv2047p92", "Vg70V"} 
}

I would like the resulting list to have the following:

  • Order first by anything looking like "T[0-9p]+K" (regex), and rearrange to have this value be first
  • Then order by "Vg[0-9p]+V", and rearrange to have it be 2nd.
  • Then order by "Ch[0-9]+", and rearrange to 3rd,
  • Then order the rest in alphabetical order, and rearrange

So, the final outcome would be:

{
 {"T0p28K", "Vg60V", "Ch70", "B0T", "Vdiv2047p92"}, 
 {"T0p28K", "Vg65V", "Ch70", "B0T", "Vdiv2047p92"},
 {"T0p36K", "Vg70V", "Ch72", "B0T", "Vdiv2047p92"}, 
 {"T0p36K", "Vg75V", "Ch72", "B0T", "Vdiv2047p92"}
}

Note: while it looks like all of the columns would be the same at the beginning, they might not always have the same position, and the number of columns themselves might change.

FWIW, eventually I will be separating the strings above with:

valRegExp = 
 RegularExpression[
  "(Vdiv|T|Vbias|Vg|B|Ch)([Ss][Ww]|[\-0-9p]+)(K|V|T)?"];
Map[Flatten[StringCases[#, 
 valRegExp :> {"$1", StringReplace["$2", "p" -> "."], 
   "($3)"}], {2}][[1]] &, list]

Is there a way to sort them "in place"? Is this better approached as a two step function: first sort, then rearrange?

EDIT Heike's answer along with a call to

Clear[SortByColumn]

SortByColumn::usage = 
"SortByColumn[List,Column,Direction]: Returns the list sorted by \
'Column' in either ascending (Direction= \"asc\") or descending \
(Direction = \"desc\") order. Default is in ascending order. For \
sorting more than one column, input Column as a list. For example, \
Column={1,2} will sort first on Column 1, then on Column 2.";

SortByColumn[list_List, col_, dir_String: "asc"] := 
 Which[dir == "desc", 
  Sort[list, ! OrderedQ[{#1[[col]], #2[[col]]}] &], dir == "asc", 
  Sort[list, OrderedQ[{#1[[col]], #2[[col]]}] &]]

Actually solves my problem, I think.

$\endgroup$
1
  • $\begingroup$ @rcollyer sorry for not posting the attempts. I had been trying to build up from first rearrange, then move, and then going with a column sort. My implementation of the rearrange was UGLY, and not really working well. However, I liked my column sort function, so I posted that at least. $\endgroup$
    – tkott
    Commented Mar 12, 2012 at 17:09

1 Answer 1

4
$\begingroup$

If all the entries in your array are strings, then one way to sort it is to do something like this:

order[str_String /; 
   StringMatchQ[str, "T" ~~ (DigitCharacter | "p") .. ~~ "K"]] := 1
order[str_String /; 
   StringMatchQ[str, "Vg" ~~ DigitCharacter .. ~~ "V"]] := 2
order[str_String /; StringMatchQ[str, "Ch" ~~ DigitCharacter ..]] := 3
order[a_] := a

#[[Ordering[order /@ #]]] & /@ list

This method uses the fact that numbers come before strings in an ordered list.

$\endgroup$
1
  • 1
    $\begingroup$ Is the Ordering construct you used in any way different from SortBy? $\endgroup$
    – Szabolcs
    Commented Mar 13, 2012 at 5:04

Your Answer

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

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