2

Say I have three lists:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
Z = [ 5,   2,   3,    1,   4,   9,   6,   0,   7]

I can sort X according to Y rather easily:

XY_sort = [x for _,x in sorted(zip(Y,X))]
print(XY_sort)  # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]

Doubts arise when I want to sort list Z according to the sorting that produced XY_sort. Ideally, I want to end up with:

Z_sort = [5,   1,   0,   2,   3,   4,   7,   9,   6]

I'm guessing the best way to do this would be to somehow store the sorted indices of X when it gets sorted into XY_sort, then using those to sort Z, but I don't know how I would go about doing this. Any help would be greatly appreciated!

0

3 Answers 3

6

You can sort the same way. It will sort first by Y, then X, if these are both the same it will sort by Z:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
Z = [ 5,   2,   3,    1,   4,   9,   6,   0,   7]


XYZ_sort = [z for y,x,z in sorted(zip(Y,X,Z))]
# [5, 1, 0, 2, 3, 4, 7, 9, 6]

[Edit thanks to the commenter's tenacity]
If it's possible that X-Y combinations are not unique, and if it's important that you preserve the relative order of Z in those cases, you can pass operator.itemgetter() as the key to the sort for a clean solution:

from operator import itemgetter

X = ["a", "b", "c", "a", "d", "e", "f", "g", "h", "i", "a"]
Y = [ 0,   1,   1,   0,  0,   1,   2,   2,   0,   1,  0]
Z = [ 5,   2,   3,  -1,  1,   4,   9,   6,   0,   7,  1]

XYZ_sort = [z for y,x,z in sorted(zip(Y,X,Z), key=itemgetter(0, 1))]

#preseves the relative order of 5, -1, and 1
# [5, -1, 1, 1, 0, 2, 3, 4, 7, 9, 6]
Sign up to request clarification or add additional context in comments.

Comments

2

You can use a key argument to call out the elements of the tuple that zip produces to sort as you desire:

>>> sorted(zip(X,Y,Z), key=lambda t: (t[1], t[0]))
[('a', 0, 5), ('d', 0, 1), ('h', 0, 0), ... ('f', 2, 9), ('g', 2, 6)]

Then filter out X and Y in the resulting tuple:

>>> [z for x,y,z in sorted(zip(X,Y,Z), key=lambda t: (t[1], t[0]))]
[5, 1, 0, 2, 3, 4, 7, 9, 6]

The advantage here is that Z is not considered in the sort order at all; only the two elements in the key function that match X and Y are used.


BTW: I usually answer all Python questions with Python 3 syntax. However, Python 2 had a really great syntax for lambda functions that would automatically unpack a tuple passed as an argument.

Python 2 only example:

>>> sorted(zip(X,Y,Z), key=lambda (x,y,z): (y, x))
[('a', 0, 5), ('d', 0, 1), ... ('f', 2, 9), ('g', 2, 6)]
>>> [z for x,y,z in sorted(zip(X,Y,Z), key=lambda (x,y,z): (y, x))]
[5, 1, 0, 2, 3, 4, 7, 9, 6]

That syntax has been retired unfortunately....


As stated in comments, this works too:

>>> [z for z,y,x in sorted(zip(Z,Y,X), key=lambda t: t[1:])]
[5, 1, 0, 2, 3, 4, 7, 9, 6]

3 Comments

You could use key=lambda t: t[:2] (if you zip(Y,X,Z)).
@superbrain: That is creative. I added that. I usually prefer to explicitly call out the elements used just so I don't have to think too much.
Hope you're a little less sad now about that loss of Python 2's syntax :-) (I liked that a lot, too...)
-1

You say "sort X according to Y", but then you really sort X according to Y and X. If you truly sorted it only by Y, you could use the same for Z as well.

2 Comments

This should be a comment, not an answer.
@MarkMeyer Nah I don't think so. I'm telling them how to achieve what they apparently really want.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.