1

I have a list1 of items:

[9, 8, 10, 2]

that I want to sort

A corresponding list2 of values for these items is: [10, 11, 8, 5]

How do I sort list1: [2 8 9 10] and maintain the same relationship to the other list so that the other list will reoorder [5, 11, 10, 8]

There was a similar question here: Python sort one list from another list but it's when we're given an ordering.

Vishal K
  • 1,368
  • 1
  • 7
  • 15
Katie Melosto
  • 1,047
  • 2
  • 14
  • 35
  • "but it's when we're given an ordering" - and you're not given an ordering here?! – mkrieger1 Jun 03 '21 at 18:27
  • sorry to be unclear. I wanted to sort the first list by least to greatest and then re-order the other list so that the same elements correspond with the elements from the first sorted list – Katie Melosto Jun 03 '21 at 18:29
  • Yes, isn't that exactly what the other question you've linked is about? – mkrieger1 Jun 03 '21 at 18:30

4 Answers4

5

One option is to zip the two lists together, sort them by the first item in each tuple of the zipped list, and then grab the second item from that sorted list.

from operator import itemgetter

fst = itemgetter(0)
snd = itemgetter(1)

[snd(tup) for tup in sorted(zip(list1, list2), key=fst)]
>> [5, 11, 10, 8]

If you need both lists as the output, then this answer does just that in the same clean fashion as this one.

gold_cy
  • 13,648
  • 3
  • 23
  • 45
  • This will not sort the first list, you'll need to call `list.sort()` again. – Guy Marino Jun 03 '21 at 18:12
  • I am aware, the output from what I understand is the second list being sorted in the same order that the first would be based on original index – gold_cy Jun 03 '21 at 18:13
  • That's true, but this requires two calls to sort, which could be costly for large lists. – Guy Marino Jun 03 '21 at 18:15
  • 1
    The final statement can be modified to retrieve all the elements for both lists, once again it is not clear what the expected output for OP is, whether it is both lists or just one. – gold_cy Jun 03 '21 at 18:16
5

You could use this:

list1, list2 = zip(*sorted(zip(list1, list2)))

or this:

order = sorted(range(len(list1)), key=lambda i: list1[i])
list1 = [list1[i] for i in order]
list2 = [list2[i] for i in order]

The first one makes a new list that has matching pairs from list1 and list2, then sorts the pairs, which will mainly focus on the values that came from list1, and use the list2 value as a tiebreaker. Then zip(*new_list) is a standard Python trick for splitting the pairs back into separate lists. It combines all the pairs together to make a new list with two long rows, one with the first item from each pair and one with the second item. Then assigning that list to two variables splits it into two lists.

The second one creates indexes for the items in both lists, then sorts the indexes using the corresponding item from list1 as a key. Then it retrieved the items from each list using the new ordering.

Or this hybrid may be more readable:

sorted_pairs = sorted(zip(list1, list2))
list1 = [v1 for v1, v2 in sorted_pairs]
list2 = [v2 for v1, v2 in sorted_pairs]
Matthias Fripp
  • 17,670
  • 5
  • 28
  • 45
  • Thank you everyone that replied. I found ```list1, list2 = zip(*sorted(zip(list1, list2)))``` to be the simplest solution. Thanks @Matthias Fripp – Katie Melosto Jun 03 '21 at 18:30
1
combined = zip(list1, list2)
combined.sort(key=lambda x: x[0])
list1 = [x[0] for x in combined]
list2 = [x[1] for x in combined]
Guy Marino
  • 429
  • 3
  • 6
1

An approach that would work with even operations other than sort, if the list elements are unique:

Code:

import random
l1 = [9, 8, 10, 2]
l2 = [10, 11, 8, 5]

my_map = dict(zip(l1, l2))

l1.sort()
l2 = [my_map[x] for x in l1]
print(l1)
print(l2)

random.shuffle(l1)
l2 = [my_map[x] for x in l1]
print(l1)
print(l2)

Output:

[2, 8, 9, 10] # l1
[5, 11, 10, 8] # l2

[9, 10, 2, 8] # l1
[10, 8, 5, 11] # l2
Abhi_J
  • 2,061
  • 1
  • 4
  • 16