Reputation: 311
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!
Upvotes: 2
Views: 91
Reputation: 92460
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]
Upvotes: 6
Reputation: 104032
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]
Upvotes: 2
Reputation: 5521
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.
Upvotes: -1