7
$\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
    Commented Nov 26, 2014 at 11:01

6 Answers 6

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
    Commented Nov 7, 2014 at 14:26
  • $\begingroup$ @rcollyer thanks! That was more or like asymptotic :D $\endgroup$
    – Yves Klett
    Commented Nov 7, 2014 at 15:39
  • $\begingroup$ So is my approach to 20k. :) $\endgroup$
    – rcollyer
    Commented Nov 7, 2014 at 15:44
  • 1
    $\begingroup$ @rcollyer it's turtles (and asymptotics) all the way down :D $\endgroup$
    – Yves Klett
    Commented Nov 7, 2014 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$
5
$\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$
0
$\begingroup$
order = {"dogs", "cats", "fish", "elephants", "giraffe", "hamsters", "poodles"};

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

Using the fact that KeyTake skips non-existing keys:

Catenate @ Values @ KeyTake[order] @ GroupBy[data, #[[3]] &]

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

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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