# How to conditionally sort a list in python with respect to two lists

## Question:

Is there a simple way to sort a list X in python using two other lists Y,Z like so?

before sorting:

``````X = ["a", "b", "c", "d"]
Y = [ 10,   1,   2,   16 ] #positive list
Z = [  5,  6,   7,  10 ] #negative list
``````

creating list W to assist with understanding

``````W = [  5,  -5,  -5,  6 ] # create new list total (Y-Z) positive - negative values.
``````

This is what I have done so far, sorted X according to W the total of Y,Z (Y-Z) in descending

• Sort X based on W (total) in descending order
``````W, X = zip(*sorted(zip(W, X),reverse=True))
X = ["d", "a", "b", "c"]
W = [  6,  5,  -5,  -5 ]
``````

What I would like to do further is conditionally sort
if values of W are equal, I’d like to sort based on highest value in list Y, lowest value in list Z and choosing the maximum.

``````X = ["a", "b", "c", "d"]
Y = [ 10,   1,   2,   16 ] #positive list
Z = [  5,  6,   7,  10 ] #negative list
W = [  5,  -5,  -5,  6 ] # create new list total (Y-Z) positive - negative values.
``````

here, indexes 1,2 are tied in W because W==W=-5
so we compare k1=max(Y,Y)=max(1,2)=2 and k2=min(Z,Z)=min(6,7)=6

• since k2>k1, we prioritize ordering of X based on Z
• if k1>k2 we should prioritize ordering of X based on Y.
final result must be,
``````X = ["d", "a", "b", "c"]
``````

How could we do this as simple as possible even for huge lists with n number of same values in the list W?

Not sure I understand the details of your sort logic because I get a different result, but the general idea is to define your sort criteria clearly as a function and then pass it to sort on your data as a list of tuples.

``````X = ["a", "b", "c", "d"]
Y = [ 10,   1,   2,   16 ] #positive list
Z = [  5,  6,   7,  10 ] #negative list

data = list(zip(X, Y, Z))

def sort_criteria(x):
return x - x, max(x, -x)

data.sort(key=sort_criteria)
print(data)
result = [item for item in data]
``````
``````[('b', 1, 6), ('c', 2, 7), ('a', 10, 5), ('d', 16, 10)]
``````

In Python 3 you could use `functools.cmp_to_key` to sort a list based on a custom comparator. In a custom corporator you could implement whatever complex logic you may think of.

``````def comparator(item1, item2):
# compares two tuples item1=(x1,y1,z1) and item2=(x2,y2,z2)
# returns:
#     1 if item1 > item2,
#    -1 if item1 < item2,
#     0 if item1 = item2
if (item1 - item1) > (item2 - item2): # W > W
return 1
elif (item1 - item1) < (item2 - item2):
return -1

if min(item1, item2) > max(item1, item2): # k2 > k1
if item1 < item2:
return 1
elif item1 > item2:
return -1
else: # k2 > k1 but z2 = z1
if item1 > item2:
return 1
elif item1 < item2:
return -1
else:
return 0
elif min(item1, item2) > max(item1, item2): # k1 > k2
if item1 > item1:
return 1
elif item1 < item2:
return -1
else: # k1 > k2 but y1 = y2
if item1 < item2:
return 1
elif item1 > item2:
return -1
else:
return 0
else: # k1 = k2
if item1 < item2: # Z1 < Z2
return 1
elif item1 > item2:
return -1
elif item1 > item2: # Z1 = Z2, Y1 > Y2
return 1
elif item1 < item2:
return -1
else:
return 0
``````

Now you could use this comparator as

``````from functools import cmp_to_key
sorted(zip(X,Y,Z), key=cmp_to_key(comparator), reverse=True)
``````

This would return

``````[('d', 16, 10), ('a', 10, 5), ('b', 1, 6), ('c', 2, 7)]
``````

Here from my point of view the actual right answer which tests for the condition according to which list the sub-sorting in case W-values are the same should be done.

To accomplish the sub-sorting W-values are now extended to a tuple with an index of item in X. After sorting the extended W the `itertools.groupby()` is used to put together items with same W-value and the indices of items in X are used to find the max-value in Y and min-value in Z required to decide about the key to sub-sorting in case of same W-values. Then the indices are further used to put the right items of X together in fully sorted order.

Here the code which accomplishes this:

``````X    = ["a", "b", "c", "d"]
Y    = [10 ,  2,   1 , 16 ] # positive list
Z    = [ 5 ,  7,   6 , 10 ] # negative list
Wsorted = sorted([ (Y[i]-Z[i], i) for i in range(len(X)) ])
Xsorted = []
import itertools
for _, g in itertools.groupby(Wsorted, lambda w: w):
lg = list(g)
if len(lg) == 1:
Xsorted += X[lg]
else:
maxY = max([Y[i] for _,i in lg])
minZ = min([Z[i] for _,i in lg])
if maxY  > minZ: lg.sort(key=lambda lgitem: Y[lgitem])
if maxY  < minZ: lg.sort(key=lambda lgitem: Z[lgitem])
if maxY == minZ: pass # no preferred sub-sorting
Xsorted += [ X[lgitem] for lgitem in lg ]
Xsorted.reverse()
print(f'{Xsorted=}') # gives: Xsorted=['d', 'a', 'b', 'c']
``````

The above code covers a general case of sorting condition based on comparison of maximum of values in Y and minimum of values in Z for same W-values to decide about sub-sorting according to Y or to Z.

The general case does not take into consideration that when W values are Y values minus Z values it doesn’t actually matter for the sub-sorting after which, Y or Z values, the sub-sorting has to be done.

It doesn’t actually matter as the order of Y values for same W values is always the same as the order of Z values because of the equal difference.

So a search for a maximum value in Y and minimum value in Z is not necessary in this special case of W and the sorting can be made much easier just using only Y values ( or Z values which gives same result ) for the sub-sorting.

Below the one liner performing such a sort for the case of the simple W being the difference between Y and Z values:

``````Xsorted = [ X[i] for i in sorted(
list(range(len(X))),key=lambda i: (Y[i]-Z[i],Y[i]),reverse=True)]
print(Xsorted) # gives ['d', 'a', 'b', 'c']
``````
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.