Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I have three parallel lists (i.e. the elements in position i of each list are related). I want to sort the first list using the function Sort and make the same changes to the other lists so that I still have parallel lists when finished.

How can I do this?

As an example: Given the lists {2, 3, 1}, {a, b, c}, and {alpha, beta, gamma}, sorting all lists according the first one gives {1, 2, 3}, {c, a, b}, and {gamma, alpha, beta}.

share|improve this question
add comment

4 Answers 4

up vote 19 down vote accepted
lists = {list1, list2, list3} = {{1, 3, 2}, {a, b, c}, {x, y, z}};

Another option

SortBy[lists\[Transpose], First]\[Transpose]

{{1, 2, 3}, {a, c, b}, {x, z, y}}

share|improve this answer
    
Why is \[Transpose] necessary? –  Tyson Williams Jun 29 '12 at 20:35
    
@TysonWilliams \[Transpose] is a nice notation (see it in the front end) for the matrix Transpose function. You first create a single list such that it's i'th element is {list1[[i]], list2[[i]], list3[[i]]}. Then, you sort that list just by looking at its elemtents' first elements –  Rojo Jun 29 '12 at 20:37
    
In other words, you start off with a matrix in which each ROW its one of your lists (thats lists), and you transpose it, and now your lists are the columns. Now you sort the rows of that matrix just by looking at the frist column, and transpose again –  Rojo Jun 29 '12 at 20:38
    
Transpose is usually a very efficient operation –  Rojo Jun 29 '12 at 20:39
1  
I accepted this solution since it is inline. –  Tyson Williams Jun 29 '12 at 20:56
add comment

You can use combination of Part and Ordering as

list1[[ Ordering @ list2 ]]

to sort list1 in the order of list2.

Examples:

{list1, list2, list3} = {{1, 3, 2}, {a, b, c}, {x, y, z}};
list2[[ Ordering @ list1 ]]

gives

{a, c, b}

and

list3[[ Ordering @ list1 ]]

gives

{x, z, y}

EDIT: Using with lists of lists, to sort the entire array based on the first list:

list = {list1, list2, list3};
list[[ All, Ordering @ list[[1]] ]]

gives

{{1, 2, 3}, {a, c, b}, {x, z, y}}

But ... as I just noticed, this is already covered in @Mr.Wizard's answer long before my edit.

share|improve this answer
    
Very good. Thanks for the solution! –  Tyson Williams Jun 29 '12 at 20:29
    
@TysonWilliams, my pleasure.. –  kguler Jun 29 '12 at 20:31
add comment

Ordering and Part is more efficient than SortBy and Transpose and it can also be done in one pass as I will demonstrate.

I create three lists of different type as described in the question:

a = RandomInteger[999, 500];
b = RandomReal[1, 500];
c = CharacterRange["a", "z"] ~RandomChoice~ 500;

I use the timeAvg function for testing:

SortBy[{a, b, c}\[Transpose], First]\[Transpose] // timeAvg

{a, b, c}[[All, Ordering@a]] // timeAvg

0.00027456

0.000026944

As can be seen second method is more than an order of magnitude faster on this data.

It is noteworthy that these two forms as shown do not perform the same operation because SortBy[list, func] is not a stable sort. Observe:

lists = {{8, 8, 6, 3, 7},
         {"i", "e", "f", "b", "m"},
         {"q", "x", "u", "w", "z"}};

SortBy[lists\[Transpose], First]\[Transpose]

lists[[All, Ordering @ First @ lists]]
{{3, 6, 7, 8, 8}, {"b", "f", "m", "e", "i"}, {"w", "u", "z", "x", "q"}}

{{3, 6, 7, 8, 8}, {"b", "f", "m", "i", "e"}, {"w", "u", "z", "q", "x"}}

You can see see that SortBy has swapped the positions of "i"/"e" and "q"/"x" in the lists so it is not a minimal reordering. This can be corrected however with a different syntax for SortBy:

SortBy[lists\[Transpose], {First}]\[Transpose]
{{3, 6, 7, 8, 8}, {"b", "f", "m", "i", "e"}, {"w", "u", "z", "q", "x"}}

This syntax also speeds up SortBy, but not enough to be competitive with Ordering:

SortBy[{a, b, c}\[Transpose], {First}]\[Transpose] // timeAvg

0.0001248

share|improve this answer
add comment

This is a more general solution that I came up with some time ago to solve some rearrangement problems. I'm pretty sure that it is slower than any of the solutions above (as it uses ReplacePart), but it is general enough to deal with any kind of ragged partitions. Usage: partitionAs sorts and partitions a list just as a reference list ref is ordered and partitioned.

partitionAs[list_, ref_] := Module[{ord = Ordering@Flatten@ref, 
    pos = Position[ref, Except[_List | List]]}, 
   ReplacePart[ref, Thread[pos -> Flatten[list][[ord]]]]];

ref = {{2, {9}, 0}, {3, 7}, 6, {4, {8, 5}, 1}}
list = {"A", "B", {"C", "D"}, "E", {"F", "G"}, "H", "I", "J"};

partitionAs[list, ref]

{{2, {9}, 0}, {3, 7}, 6, {4, {8, 5}, 1}}

{{"C", {"J"}, "A"}, {"D", "G"}, "I", {"F", {"E", "H"}, "B"}}

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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