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
    $\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}}*)
| improve this answer | |
$\endgroup$
  • $\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}|>
| improve this answer | |
$\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.

| improve this answer | |
$\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}} *)
| improve this answer | |
$\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]] &]
| improve this answer | |
$\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.