假设我有两个列表:
list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
如果我运行 list1.sort()
,它会将其排序到 [1,1,2,3,4]
但有没有办法得到list2
也同步(所以我可以说项目 4
属于 'three'
)?所以,预期的输出是:
list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']
我的问题是我有一个非常复杂的程序,可以很好地处理列表,但我有点需要开始引用一些数据。我知道这对于字典来说是一个完美的情况,但我试图在处理过程中避免使用字典,因为我确实需要对键值进行排序(如果我必须使用字典,我知道如何使用它们)。
基本上这个程序的本质是,数据以随机顺序出现(如上),我需要对其进行排序,处理然后发送结果(顺序无关紧要,但用户需要知道哪个结果属于哪个键)。我考虑过先将其放入字典中,然后对列表进行排序,但如果不维护顺序,我将无法区分具有相同值的项目(在将结果传达给用户时可能会产生影响)。所以理想情况下,一旦我得到列表,我宁愿想办法将两个列表排序在一起。这可能吗?
最佳答案
解决这个问题的一个经典方法是使用“装饰、排序、取消装饰”的习语,使用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']
值得注意的是,上面可能会为了简洁而牺牲速度;占用 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's suggestion仍然快一点,但它可能只会快一点,因为 Python 使用 very same DSU idiom internally对于所有基于键的排序。它只是发生在更接近裸机的地方。 (这显示了 zip
例程的优化程度!)
我认为基于 zip
的方法更灵活,可读性更强,所以我更喜欢它。
请注意,当 list1
的元素相等时,这种方法最终会比较 list2
的元素。如果 list2
的元素不支持比较,或者比较时不产生 bool 值(例如,如果 list2
是 NumPy 数组的列表),这将失败, 如果 list2
的元素比较昂贵,最好还是避免比较。
在这种情况下,您可以按照 jfs 的答案中的建议对索引进行排序,或者您可以为排序提供一个避免比较 list2
元素的关键函数:
result1, result2 = zip(*sorted(zip(list1, list2), key=lambda x: x[0]))
此外,当输入为空时,使用 zip(*...)
作为转置会失败。如果您的输入可能为空,您将不得不单独处理这种情况。
关于python - 如何以完全相同的方式对两个列表(相互引用)进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9764298/