# Efficient Indexed Sort

Suppose I have a preference for animals given by:

orderOfAnimals = {"dogs", "cats", "fish", "elephants", "giraffe", "hamsters", "poodles"};


I'm given many data with individual elements that look as follows:

exampleData = {{1, 2, "cats", 3}, {2, 4, "elephants", 6}, {6, 8,"dogs", 7}, {5, 7, "fish",7}}


For display purposes I want to order the data according to my preference of animals, but to keep track of which element is which. In this example:

reordering = {3, 1, 4, 2};
Grid[exampleData[[reordering]]]


In truth my animal preference is much more detailed, how could I efficiently calculate the reordering list?

Update

Many thanks for current answers, I'm reviewing them. If it's easier the data can be modified into Rules, Associations or Dataset. It's the flexibility/efficiency of reordering that I'm interested in, it might be easier to operate on something like this:

assocData =Association @@ Thread[{"a", "b", "Animal", "c"} -> #] & /@ exampleData;


Great answers, really appreciate them. The one I found most useful, and now exists (cited) in a deployed application, is Yves Klett. It was the simplest and most directly usable on my Lists and Associations

gpap was helpful with associations, I agree about there being a better way to check multiple keys against an association - currently looking into this myself.

Kguler gets the reward for most solutions in one answer - almost won it for me on MapIndexed[Rule,expr].

• Thanks for the accept! Always happy to help out at the zoo :D Nov 26 '14 at 11:01

pos=Flatten[(Position[exampleData, #, {2}] & /@ orderOfAnimals), 1][[All, 1]]

(*{3, 1, 4, 2}*)

exampleData[[pos]]

(*{{6, 8, "dogs", 7}, {1, 2, "cats", 3}, {5, 7, "fish"}, {2, 4, "elephants", 6}}*)

• congrats on hitting 10k! Nov 7 '14 at 14:26
• @rcollyer thanks! That was more or like asymptotic :D Nov 7 '14 at 15:39
• So is my approach to 20k. :) Nov 7 '14 at 15:44
• @rcollyer it's turtles (and asymptotics) all the way down :D Nov 7 '14 at 16:25

You can also make an association and use the keys directly. Here's your data

exampleData = {{1, 2, "cats", 3}, {2, 4, "elephants", 6}, {6, 8,
"dogs", 7}, {5, 7, "fish"}};
orderOfAnimals = {"dogs", "cats", "fish", "elephants", "giraffe",
"hamsters", "poodles"};


and the Association object:

animalsTonumbers =
exampleData[[All, 3]] -> (Drop[#, {3}] & /@ exampleData)]


and the data (values) based on your ordering - I am selecting the keys that exist in the association. I seem to remember this can be done more tersely but I can't find how right now. Anyway, values are easy to extract:

animalsTonumbers /@
Select[orderOfAnimals, KeyExistsQ[animalsTonumbers, #] &]
(*out*) {{6, 8, 7}, {1, 2, 3}, {5, 7}, {2, 4, 6}}


or if you want the keys as well:

animalsTonumbers[[Select[orderOfAnimals, KeyExistsQ[animalsTonumbers, #] &]]]
(*out*) <|"dogs" -> {6, 8, 7}, "cats" -> {1, 2, 3}, "fish" -> {5, 7}, "elephants" -> {2, 4, 6}|>

orderOfAnimals = {"dogs", "cats", "fish", "elephants", "giraffe",
"hamsters", "poodles"};
MapIndexed[(f[#1] = First@#2) &, orderOfAnimals];
exampleData = {{1, 2, "cats", 3}, {2, 4, "elephants", 6}, {6, 8,
"dogs", 7}, {5, 7, "fish"}};
SortBy[exampleData, f[#[[3]]] &]


yields:

 (*{{6, 8, "dogs", 7}, {1, 2, "cats", 3}, {5, 7, "fish"}, {2, 4,
"elephants", 6}}*)


UPDATE Not as instructive as gpap but also exploiting Association:

fun[x_] :=
With[{a = Association[# -> {} & /@ orderOfAnimals]},
Flatten[orderOfAnimals /.
Merge[{a, GroupBy[exampleData, #[[3]] &]}, Join @@ # &], 1]]


applying:fun[exampleData] yields same result.

I have not separated out numbers.

ClearAll[f1, f2, f3];
f1[ord_] = Module[{o = Ordering[#[[All, 3]] /. Dispatch@MapIndexed[Rule, ord]]}, #[[o]]] &;
f2[ord_] := With[{rule = Dispatch@MapIndexed[Rule, ord]}, SortBy[#, #[[3]] /. rule &]] &;
f3[ord_] := With[{p = Reverse@Flatten[Position[ord, #] & /@ #[[All, 3]], 2]}, #[[p]]] &;


Example:

exampleData = {{1, 2, "cats", 3}, {2, 4, "elephants", 6}, {6, 8, "dogs", 7}, {5, 7, "fish"}};
orderOfAnimals = {"dogs", "cats", "fish", "elephants", "giraffe", "hamsters", "poodles"};

Equal @@ {f1[orderOfAnimals][exampleData], f2[orderOfAnimals][exampleData],
f3[orderOfAnimals][exampleData]}
(* True *)

f1[orderOfAnimals][exampleData]
(* {{6,8,"dogs",7},{1,2,"cats",3},{5,7,"fish"},{2,4,"elephants",6}} *)

position = AssociationThread[
orderOfAnimals, Range@Length@orderOfAnimals];

(* <|"dogs" -> 1, "cats" -> 2, "fish" -> 3, .. *)

SortBy[exampleData, position[#[[3]]] &]

(* {{6, 8, "dogs", 7}, {1, 2, "cats", 3}, .. *)


Is animal always the third in a datum? Else search for the animal:

SortBy[exampleData, With[{
animal = Cases[#, _String, 1, 1][[1]]},
position[animal]] &]