sort list a based on list b, but maintain original order of list a wherever b values are equal

Viewed 61

I would like to sort list A based on list B(descending order) such that if two list A,B are

A=["k","a","l","d"]
B=[4,3,7,22]

after sorting, ( sort A based on desc(B) ) A=["d","l","k","a"]

However if B=[0,0,0,0] I would like A to remain A=["k","a","l","d"] after sorting too

so wherever values are equal, the original order must be retained. if B=[0,22,5,0] and A=["k","a","l","d"] then, after sorting, A=["a","l","k","d"]

I have tried using A= [x for _,x in sorted(zip(B,A),reverse=True)],

  • with if A=["k","a","l","d"] and B=[0,0,0,0], this gives A=["a","d","k","l"] but I want A to remain A=["k","a","l","d"]
  • with if A=["k","a","l","d"] and B=[0,22,5,0], this gives A=["a","l","d","k"] but I want A to be A=["a","l","k","d"]

How do I achieve this?

3 Answers

Use the key parameter of sorted

A = ["k", "a", "l", "d"]
Bs = [[4, 3, 7, 22], [0, 0, 0, 0], [0, 22, 5, 0]]

for B in Bs:
    res = [a for _, a in sorted(enumerate(A), key=lambda x: B[x[0]], reverse=True)]
    print(B, res)

Output

[4, 3, 7, 22] ['d', 'l', 'k', 'a']
[0, 0, 0, 0] ['k', 'a', 'l', 'd']
[0, 22, 5, 0] ['a', 'l', 'k', 'd']

As an alternative, you could use:

it_B = iter(B)
res = sorted(A, key=lambda _: next(it_B), reverse=True)

Both ideas work because Timsort (the underlying algorithm of sorted) is stable. Since you are using just one key, when the values are equal, they conserve the appearance order.

Easiest method I've found is to use numpy's Argsort.

index = np.argsort(B)
a = a[index]

Order is kept if they are all the same (i.e. [0,0,0,0]).

Related