Suppose I have a list called roster
. Each element in roster
is a list containing two strings: (i) the species of animal and (ii) the animal's name.
roster = {
{"cat", "Garfield"},
{"cat", "Cheshire"},
{"dog", "Pongo"},
{"dog", "Lassie"},
{"elephant", "Horton"},
{"elephant", "Babar"}
};
I want to sort roster
in descending order by species size: elephants are largest, dogs are smaller, and cats are smallest. However, in doing the sort, I want to keep the order of names unchanged.
One way to do this is to apply an ordering list to roster
:
roster[[{5, 6, 3, 4, 1, 2}]]
{{"elephant", "Horton"}, {"elephant", "Babar"}, {"dog", "Pongo"}, {"dog", "Lassie"}, {"cat", "Garfield"}, {"cat", "Cheshire"}}
which is the desired output.
However, I'd like to achieve this result more programmatically.
I've considered using SortBy
[list, f], which according to the documentation, "sorts the elements of list in the order defined by applying f to each of them." I've tried the following:
(* Sort in decending order by size: elephant (largest), dog, cat (smallest) *)
sortFunc[animals_List] := Which[#[[1]] == "elephant", 1, #[[2]] == "dog", 2, #[[3]] == "cat", 3] &;
SortBy[roster, sortFunc]
which gives the correct species order, but also sorts the elements so the animal names are in canonical order, which is not what I want. I want the names to appear in whatever order they are given in roster
. How can I do this?
I would like the method to be usable in Mathematica 9.0+, so please do not use Association
s or other "new" built-in features (with "new" being features implemented in Mathematica within the past 8-10 years).
Additionally, I would like the method to be generalizable to nonconsecutive species in roster
. For example, the following
roster = {
{"cat", "Garfield"},
{"cat", "Cheshire"},
{"dog", "Pongo"},
{"dog", "Lassie"},
{"elephant", "Horton"},
{"elephant", "Babar"},
{"dog", "Perdita"},
{"dog", "Snowy"},
{"dog", "Odie"}
};
should be sorted as the following (i.e., leaving the order of names unchanged within each species):
{
{"elephant", "Horton"},
{"elephant", "Babar"},
{"dog", "Pongo"},
{"dog", "Lassie"},
{"dog", "Perdita"},
{"dog", "Snowy"},
{"dog", "Odie"},
{"cat", "Garfield"},
{"cat", "Cheshire"}
}
sortFunc
given by @Bob Hanlon, compareSortBy[roster, {sortFunc}]
(giving a stable sort) withSortBy[roster, sortFunc]
. As I understand things from this answer,SortBy[list, {fn}]
gives a stable sort (ties are not sorted) whereasSortBy[list, fn]
will sort ties $\endgroup$