假设我有两个列表:

list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

如果我运行list1.sort(),它会把它排序到[1,1,2,3,4],但是否有一种方法让list2同步(所以我可以说项目4属于' 3 ')?因此,期望输出为:

list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']

我的问题是,我有一个相当复杂的程序,它可以很好地处理列表,但我需要开始引用一些数据。我知道这对字典来说是一个完美的情况,但我试图在我的处理中避免字典,因为我确实需要对键值进行排序(如果我必须使用字典,我知道如何使用它们)。

Basically the nature of this program is, the data comes in a random order (like above), I need to sort it, process it and then send out the results (order doesn't matter but users need to know which result belongs to which key). I thought about putting it in a dictionary first, then sorting list one but I would have no way of differentiating of items in the with the same value if order is not maintained (it may have an impact when communicating the results to users). So ideally, once I get the lists I would rather figure out a way to sort both lists together. Is this possible?


当前回答

是什么:

list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

sortedRes = sorted(zip(list1, list2), key=lambda x: x[0]) # use 0 or 1 depending on what you want to sort
>>> [(1, 'one'), (1, 'one2'), (2, 'two'), (3, 'three'), (4, 'four')]

其他回答

我使用senderle给出的答案已经很长时间了,直到我发现了np.argsort。 下面是它的工作原理。

# idx works on np.array and not lists.
list1 = np.array([3,2,4,1])
list2 = np.array(["three","two","four","one"])
idx   = np.argsort(list1)

list1 = np.array(list1)[idx]
list2 = np.array(list2)[idx]

我觉得这个解决方案更直观,而且效果很好。本:

def sorting(l1, l2):
    # l1 and l2 has to be numpy arrays
    idx = np.argsort(l1)
    return l1[idx], l2[idx]

# list1 and list2 are np.arrays here...
%timeit sorting(list1, list2)
100000 loops, best of 3: 3.53 us per loop

# This works best when the lists are NOT np.array
%timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 2.41 us per loop

# 0.01us better for np.array (I think this is negligible)
%timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best for 3 loops: 1.96 us per loop

即使np。argsort不是最快的,我发现它更容易使用。

解决这个问题的一个经典方法是使用“装饰,排序,不装饰”习语,使用python内置的zip函数尤其简单:

>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2 
('one', 'one2', 'two', 'three', 'four')

这些当然不再是列表,但如果重要的话,很容易补救:

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']

值得注意的是,上述方法可能会为了简洁而牺牲速度;in-place版本占用3行,在我的机器上处理小列表时稍微快一点:

>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop

另一方面,对于更大的列表,单行版本可能更快:

>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop

正如Quantum7所指出的,JSF的建议还是快了一点,但它可能只会快一点点,因为Python在内部对所有基于键的排序使用了完全相同的DSU习惯用法。只是发生在离裸露的金属更近的地方。(这显示了zip例程优化得多么好!)

我认为基于zip的方法更灵活,可读性更强,所以我更喜欢它。


注意,当list1的元素相等时,此方法将最终比较list2的元素。如果list2的元素不支持比较,或者在比较时不产生布尔值(例如,如果list2是NumPy数组的列表),这将失败,如果list2的元素比较代价非常高,那么避免比较可能会更好。

在这种情况下,你可以按照jfs的答案对索引进行排序,或者你可以给排序一个键函数,以避免比较list2的元素:

result1, result2 = zip(*sorted(zip(list1, list2), key=lambda x: x[0]))

同样,当输入为空时,使用zip(*…)作为转置也会失败。如果输入可能为空,则必须单独处理这种情况。

Schwartzian变换。内置的Python排序是稳定的,所以两个1不会造成问题。

>>> l1 = [3, 2, 4, 1, 1]
>>> l2 = ['three', 'two', 'four', 'one', 'second one']
>>> zip(*sorted(zip(l1, l2)))
[(1, 1, 2, 3, 4), ('one', 'second one', 'two', 'three', 'four')]

如果你正在使用numpy,你可以使用np。Argsort来获取排序后的索引,并将这些索引应用到列表中。这适用于你想要排序的任意数量的列表。

import numpy as np

arr1 = np.array([4,3,1,32,21])
arr2 = arr1 * 10
sorted_idxs = np.argsort(arr1)

print(sorted_idxs)
>>> array([2, 1, 0, 4, 3])

print(arr1[sorted_idxs])
>>> array([ 1,  3,  4, 21, 32])

print(arr2[sorted_idxs])
>>> array([ 10,  30,  40, 210, 320])

如果你需要同步排序超过2个列表,我想建议一个解决方案:

def SortAndSyncList_Multi(ListToSort, *ListsToSync):
    y = sorted(zip(ListToSort, zip(*ListsToSync)))
    w = [n for n in zip(*y)]
    return list(w[0]), tuple(list(a) for a in zip(*w[1]))