1. ホーム
  2. python

[解決済み] 2つのリスト(お互いを参照している)を全く同じ方法でソートする方法

2022-02-07 15:39:39

質問

2つのリストがあるとします。

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']

私の問題は、リストでうまく動作しているかなり複雑なプログラムがあるのですが、あるデータを参照し始める必要があることです。これは辞書にとって完璧な状況であることは分かっていますが、私はキー値をソートする必要があるので、私の処理では辞書を避けようとしています(もし私が辞書を使用しなければならないなら、私はそれを使用する方法を知っています)。

基本的にこのプログラムの本質は、データがランダムな順序で来る(上記のように)、私はそれをソートし、それを処理し、その後結果を送信する必要があります(順序は重要ではありませんが、ユーザーはどの結果がどのキーに属しているかを知る必要があります)。まず辞書に入れてからリスト1をソートすることも考えましたが、順序が維持されないと、同じ値を持つ項目を区別する方法がありません(ユーザーに結果を伝えるときに影響があるかもしれません)。 そのため、理想的には、リストを取得したら、両方のリストを一緒にソートする方法を考えたいと思っています。 これは可能でしょうか?

どのように解決するのですか?

この問題に対する古典的なアプローチの1つは、quot;decorate, sort, undecorate"というイディオムを使うことで、これは特に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の提案 はまだ少し速いのですが、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(*...) は、入力が空の場合、転置に失敗します。入力が空になる可能性がある場合、その場合は別途対処する必要があります。