Home > Software design >  How to conditionally sort a list in python with respect to two lists
How to conditionally sort a list in python with respect to two lists

Time:07-20

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[1]==W[2]=-5 so we compare k1=max(Y[1],Y[2])=max(1,2)=2 and k2=min(Z[1],Z[2])=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?

CodePudding user response:

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[1] - x[2], max(x[1], -x[2])

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

CodePudding user response:

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[1] - item1[2]) > (item2[1] - item2[2]): # W[1] > W[2]
        return 1
    elif (item1[1] - item1[2]) < (item2[1] - item2[2]):
        return -1
    
    if min(item1[2], item2[2]) > max(item1[1], item2[1]): # k2 > k1
        if item1[2] < item2[2]:
            return 1
        elif item1[2] > item2[2]:
            return -1
        else: # k2 > k1 but z2 = z1
            if item1[1] > item2[1]:
                return 1
            elif item1[1] < item2[1]:
                return -1
            else:
                return 0
    elif min(item1[2], item2[2]) > max(item1[1], item2[1]): # k1 > k2
        if item1[1] > item1[2]:
            return 1
        elif item1[2] < item2[1]:
            return -1
        else: # k1 > k2 but y1 = y2
            if item1[2] < item2[2]:
                return 1
            elif item1[2] > item2[2]:
                return -1
            else:
                return 0
    else: # k1 = k2
        if item1[2] < item2[2]: # Z1 < Z2
            return 1
        elif item1[2] > item2[2]:
            return -1
        elif item1[1] > item2[1]: # Z1 = Z2, Y1 > Y2
            return 1
        elif item1[1] < item2[1]:
            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)]
  • Related