dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

python でリストを複数項目でソートする方法

2021-02-11 12:55:25 | python
python で、リストの要素内に複数の項目でソートを行う方法のメモ。

str に文字列、num に数字が入っているデータをソートします。
data1 = [
    {'str': 'a', 'num': 6},
    {'str': 'a', 'num': 5},
    {'str': 'b', 'num': 4},
    {'str': 'b', 'num': 3},
    {'str': 'c', 'num': 2},
    {'str': 'c', 'num': 1},
]

str の昇順、num の昇順 でソートする場合には、lambda で str、num の順で指定します。
sorted(data1, key=lambda x: (x['str'], x['num']))
-->
[{'str': 'a', 'num': 5},
 {'str': 'a', 'num': 6},
 {'str': 'b', 'num': 3},
 {'str': 'b', 'num': 4},
 {'str': 'c', 'num': 1},
 {'str': 'c', 'num': 2}]

str の昇順、num の昇順 でソートする場合には、lambda で str、num の順で指定します。
sorted(data1, key=lambda x: (x['num'], x['str']))
-->
[{'str': 'c', 'num': 1},
 {'str': 'c', 'num': 2},
 {'str': 'b', 'num': 3},
 {'str': 'b', 'num': 4},
 {'str': 'a', 'num': 5},
 {'str': 'a', 'num': 6}]

str の降順、num の降順 でソートする場合には、reverse=True を指定します。
sorted(data1, key=lambda x: (x['str'], x['num']), reverse=True)
-->
[{'str': 'c', 'num': 2},
 {'str': 'c', 'num': 1},
 {'str': 'b', 'num': 4},
 {'str': 'b', 'num': 3},
 {'str': 'a', 'num': 6},
 {'str': 'a', 'num': 5}]


より自由に比較順を制御する必要がある場合には functools モジュールの cmp_to_key を使います。
from functools import cmp_to_key

# str の降順、num の昇順でソート
def cmp1(a, b):
    # str を比較し、比較結果の符号を反転して返す
    cmp_str = 0 if a['str'] == b['str'] else -1 if a['str'] < b['str'] else 1
    if cmp_str != 0:
        return - cmp_str

    # a['str'] が一致した場合は、num の比較結果を返す
    cmp_num = 0 if a['num'] == b['num'] else -1 if a['num'] < b['num'] else 1
    return cmp_num

# str の昇順、num の降順でソート
def cmp2(a, b):
    # str を比較し、比較結果を返す
    cmp_str = 0 if a['str'] == b['str'] else -1 if a['str'] 

比較関数に cmp1 を指定し、str の降順、num の昇順でソート
sorted(data1, key=cmp_to_key(cmp1)))
-->
[{'str': 'c', 'num': 1},
 {'str': 'c', 'num': 2},
 {'str': 'b', 'num': 3},
 {'str': 'b', 'num': 4},
 {'str': 'a', 'num': 5},
 {'str': 'a', 'num': 6}]

比較関数に cmp2 を指定し、str の昇順、num の降順でソート
sorted(data1, key=cmp_to_key(cmp2))
-->
[{'str': 'a', 'num': 6},
 {'str': 'a', 'num': 5},
 {'str': 'b', 'num': 4},
 {'str': 'b', 'num': 3},
 {'str': 'c', 'num': 2},
 {'str': 'c', 'num': 1}]

num のソート順に関しては、符号の反転でも実現可能
sorted(data1, key=lambda x: (x['str'], - x['num']), reverse=True)
->
[{'str': 'c', 'num': 1},
 {'str': 'c', 'num': 2},
 {'str': 'b', 'num': 3},
 {'str': 'b', 'num': 4},
 {'str': 'a', 'num': 5},
 {'str': 'a', 'num': 6}]