Pythonで2つのリストを関係を保持したままソートする

Pythonで2つの配列を関係を保持したままソートする方法


例えば以下のような2つのリストをソートする場合を考えます

    list1 = [1, 3, 5, 2, 7, 9, 4]
    list2 = ['one', 'three', 'five', 'two', 'seven', 'nine', 'four']

list1を昇順でソートする場合にはsorted()を使用します

>>> list1 = [1, 3, 5, 2, 7, 9, 4]
>>> sorted(list1)
[1, 2, 3, 4, 5, 7, 9]

ここでlist2list1のソートに合わせて

>>> list2 = ['one', 'two', 'three', 'four', 'five', 'seven', 'nine']

のようにソートする場合は少し手間がかかりそうですが、zip()関数を使用すると一行で記述できます

>>> list1 = [1, 3, 5, 2, 7, 9, 4]
>>> list2 = ['one', 'three', 'five', 'two', 'seven', 'nine', 'four']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 2, 3, 4, 5, 7, 9)
>>> list2
('one', 'two', 'three', 'four', 'five', 'seven', 'nine')

何がおこったのかを詳しく見ていきます

zip()関数は複数のイテレータを受け取り、要素毎にタプルでグループ化したリスト (イテレータ)を生成してくれます。上記コードの内側のzip()は以下のようなリストを作成します

>>> list(zip(list1, list2))
[(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five'), (7, 'seven'), (9, 'nine')]

このリストをsorted()関数でソートします

>>> sorted(zip(list1, list2))
[(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five'), (7, 'seven'), (9, 'nine')]

sort()はデフォルトでは最初の要素をキーとしてソートするため、整数の昇順にソートされます。仮に2番目のリストの要素でソートしたい場合は以下のようにkey引数で指定します

>>> sorted(zip(list1, list2), key=lambda x: x[1])
[(5, 'five'), (4, 'four'), (9, 'nine'), (1, 'one'), (7, 'seven'), (3, 'three'), (2, 'two')]

上記のようにソートされた2つの要素からなるリストを再度外側のzip()に渡して、最初の要素 (list1の要素)と2番目の要素(list2の要素)をグループ化したリストに分解します

>>> list(zip(*sorted(zip(list1, list2))))
[(1, 2, 3, 4, 5, 7, 9), ('one', 'two', 'three', 'four', 'five', 'seven', 'nine')]

この時zip()関数に渡す際に上記のように*でアンパックする必要があります。アンパックを忘れると[(1, 'one'), (2, 'two'), ...]のような1つのリストを渡すことになるため、zip()も1つのリストを含んだ1つのタプルを返してくれるだけになります。

最後に、この方法は3つ以上のリストをソートする場合も同様です

>>> list1 = [1, 3, 5, 2, 7, 9, 4]
>>> list2 = ['one', 'three', 'five', 'two', 'seven', 'nine', 'four']
>>> list3 = ['ichi', 'san', 'go', 'ni', 'nana', 'kyu', 'yon']
>>> list(zip(*(zip(list1, list2, list3))))
[(1, 3, 5, 2, 7, 9, 4),
 ('one', 'three', 'five', 'two', 'seven', 'nine', 'four'),
 ('ichi', 'san', 'go', 'ni', 'nana', 'kyu', 'yon')]

おすすめ