6
$\begingroup$

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;

Answer Selection

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].

$\endgroup$
1
  • 1
    $\begingroup$ Thanks for the accept! Always happy to help out at the zoo :D $\endgroup$
    – Yves Klett
    Nov 26 '14 at 11:01
4
$\begingroup$

What about:

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}}*)
$\endgroup$
4
  • $\begingroup$ congrats on hitting 10k! $\endgroup$
    – rcollyer
    Nov 7 '14 at 14:26
  • $\begingroup$ @rcollyer thanks! That was more or like asymptotic :D $\endgroup$
    – Yves Klett
    Nov 7 '14 at 15:39
  • $\begingroup$ So is my approach to 20k. :) $\endgroup$
    – rcollyer
    Nov 7 '14 at 15:44
  • 1
    $\begingroup$ @rcollyer it's turtles (and asymptotics) all the way down :D $\endgroup$
    – Yves Klett
    Nov 7 '14 at 16:25
6
$\begingroup$

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 = 
 AssociationThread[
  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}|>
$\endgroup$
4
$\begingroup$
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.

$\endgroup$
4
$\begingroup$
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}} *)
$\endgroup$
3
$\begingroup$
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]] &]
$\endgroup$

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.