13
$\begingroup$

I was playing with Sort. All examples I have come across so far, i.e

Sort[{1, -1, 3, -3, 2, 5}, Abs[#1]<Abs[#2]&] 

can expressed in a shorter fashion using SortBy

SortBy[{1, -1, 3, -3, 2, 5}, Abs] 

Surely, they wouldn't put in a completely redundant function, so what are the preferred use cases for Sort?

$\endgroup$
24
$\begingroup$

In general SortBy can do pretty much anything that Sort does; in some cases, possibly better or faster. You can find many comparisons on this site if you just search for both function names.

I also disagree with @user21382 that his task could not be expressed elegantly in SortBy form: not only can it be done, I would actually argue that it could be done even more readably with SortBy than with Sort.

User21382 set the task to sort the following data, presented as an association, first by ascending age, then by descending alphabetical order. This can be accomplished using the fact that SortBy can take a list of functions that are applied in sequence to break ties:

data = {
  <|"Name"->"Jill", "Age" -> 23|>,
  <|"Name"->"Jack", "Age" -> 55|>,
  <|"Name"->"Jen", "Age" -> 55|>,
  <|"Name"->"Joe", "Age" -> 23|>
};

SortBy[data, 
  {
  (#["Age"]&), (* by age, ascending *)
  (Total@ ToCharacterCode@ ToUpperCase@ #["Name"]&) (* by alpha order, descending *)
  }
]

(* Out:
{
<|"Name" -> "Joe", "Age" -> 23|>,
<|"Name" -> "Jill", "Age" -> 23|>,
<|"Name" -> "Jen", "Age" -> 55|>,
<|"Name" -> "Jack", "Age" -> 55|>
}
*)

Here I take advantage of the fact that higher character codes correspond to letters further down in alphabetical order, so ordering by increasing character code effectively gives reverse alphabetical order.

SortBy also has an operator syntax, i.e. the following two usages are equivalent:

SortBy[data, sortingfunction] == SortBy[sortingfunction] [data]

I find the latter form very readable and highly expressive.

Another interesting property of SortBy is the fact that it gives ready access to a stable sort function, i.e. a sorting algorithm that maintains the relative order of records with equal values, by using the following syntax:

SortBy[ data, {sortingfunction} ]

In this case, no tie-breaking function is provided, so tied values will be left in the original order. This has been showcased multiple times on the site already, so I will just link to this older answer from StackOverflow that explains the point very nicely.

$\endgroup$
  • 7
    $\begingroup$ I wish I could +2 for mentioning stable sort and even linking to the best origin post for it. $\endgroup$ – Mr.Wizard May 9 '15 at 6:49
  • 1
    $\begingroup$ @Mr.Wizard Thank you! That's very kind. $\endgroup$ – MarcoB May 9 '15 at 14:24
10
$\begingroup$

I commented on this a bit here: What are the most common pitfalls awaiting new users?

Since SortBy was introduced in Mathematica 6 it is preferred where applicable over Sort with a custom comparator. This is because SortBy uses vector application of its second argument over the sort expression then the fast internal sort/ordering function, whereas Sort results in high-level application to pairs of values. Not all Sort operations can be practically converted into SortBy ones however. (As far as I know.)

To highlight the behavior of Sort consider:

Sort[Range[7], (Print[{#, #2}]; #2 - # >= 3) &]

{1,2}

{1,3}

{2,3}

{4,5}

{6,7}

{4,7}

{1,5}

{3, 2, 1, 5, 4, 7, 6}

Individual pairs of elements are compared and blocks of elements are reordered if a test returns False. This is simply not the way that SortBy uses its second parameter. Admittedly I struggle to think of examples where this behavior of Sort is needed.

$\endgroup$
10
$\begingroup$

No one has mentioned the difference in performance:

SeedRandom[0];
data = RandomInteger[{-10000, 10000}, 10^5];

sdata = Sort[data, Abs[#1] < Abs[#2] &]; // AccurateTiming
sbdata = SortBy[data, Abs]; // AccurateTiming
sbstable = SortBy[data, {Abs}]; // AccurateTiming
(*
  1.86124
  0.0222057
  0.00999333
*)

As mentioned by MarcoB, the results are not always equivalent, since the second SortBy does a stable sort and apparently Sort and SortBy do different tie-breaks:

sdata[[10 ;; 20]]
sbdata[[10 ;; 20]]
sbstable[[10 ;; 20]]
(*
  {-1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1}
  {-1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1}
  {-1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1}
*)
$\endgroup$
  • $\begingroup$ I would include a BenchMarkPlot but it's not working this morning -- emits SymbolName::sym errors. Is this a known problem in V10.1 (Mac OS, if that's important)? $\endgroup$ – Michael E2 May 9 '15 at 12:17
  • 1
    $\begingroup$ FYI I did mention the difference in performance, and I also linked to an answer of mine with a graph. I forgot to include the stable performance however though I have written about it before. +1 for the order differences however. $\endgroup$ – Mr.Wizard May 9 '15 at 23:45
  • $\begingroup$ @Mr.Wizard I misunderstood your reference. Perhaps because it was late. Sorry about that. Thanks for the upvote, in any case. $\endgroup$ – Michael E2 May 10 '15 at 0:13
5
$\begingroup$

SortBy is usually a more concise form of Sort, yes, but at times, you may have data that cannot be expressed, or cannot be expressed elegantly, using SortBy. For example, let's suppose that we want to sort the following by age, ascending, then by name, descending:

data = {
  <|"Name"->"Jill", "Age" -> 23|>,
  <|"Name"->"Jack", "Age" -> 55|>,
  <|"Name"->"Jen", "Age" -> 55|>,
  <|"Name"->"Joe", "Age" -> 23|>};
Sort[
  data,
  Function@Or[
    #1["Age"] < #2["Age"],
    #1["Age"] == #2["Age"] && OrderedQ[{#2["Name"], #1["Name"]}]]]

{<|"Name" -> "Joe", "Age" -> 23|>, <|"Name" -> "Jill", "Age" -> 23|>, <|"Name" -> "Jen", "Age" -> 55|>, <|"Name" -> "Jack", "Age" -> 55|>}

Admittedly, I almost never use Sort unless I can just call Sort[data], but it has its uses on occasion.

$\endgroup$
  • 4
    $\begingroup$ It should be noted that SortBy was new in v6 and has an operator form, while Sort doesn't, and is older. $\endgroup$ – evanb May 8 '15 at 21:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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