In order to sort alphanumeric-as-string data of the form {"T3", "T14", "T1", "E2"}, so that "T14" comes after "T3", SortBy requires the tiebreaker function list:
{StringTake[#, 1] &, ToExpression@StringDrop[#, 1] &}
Which works as intended when this expression is inserted literally in SortBy. However, defining a utility function
mySort[x_String]:={StringTake[x, 1] , ToExpression@StringDrop[x, 1]}
doesn't work since the output is a list of expressions rather than a list of functions.
The alternative - to define a function via
mySort := {StringTake[#, 1] &, ToExpression@StringDrop[#, 1] &}
only works when the list to be sorted is 1-dimensional (as above) but not with lists of the form data2={"T3"->a, "T14"->b, "T1"->c, "E2"->d} where it is necessary to use SortBy[data2,mySort[#[[1]]]&].
Any alternatives that will work for general expressions?
Answer
I think this question admits an elegant solution. Here is my attempt: define a special wrapper:
ClearAll[sortFun];
sortFun /: SortBy[expr_, sortFun[funs_List, partFun_]] :=
SortBy[expr, Map[Composition[#, partFun] &, funs]];
Now,
mySort := {StringTake[#, 1] &, ToExpression@StringDrop[#, 1] &}
and
SortBy[{"T3","T14","T1","E2"}, sortFun[mySort, Identity]]
(* {E2,T1,T3,T14} *)
while
SortBy[{"T3"->a,"T14"->b,"T1"->c,"E2"->d}, sortFun[mySort, First]]
(* {E2->d,T1->c,T3->a,T14->b} *)
EDIT
Perhaps more elegantly
ClearAll[sortFun];
sortFun /: SortBy[expr_, sortFun[funs_List, partFun_]] :=
SortBy[expr, Thread[Composition[funs, partFun]]];
and just for fun, another version:
ClearAll[sortFun];
sortFun /: call : SortBy[expr_, sortFun[funs_List, partFun_]] :=
Block[{sortFun = Thread[Composition[##]] &}, call];
Comments
Post a Comment