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


画像形式の判定方法

2021-02-06 13:15:10 | linux
画像形式をデータから判定する方法のメモ。

画像ファイルの形式は、画像データの先頭のバイト列のマジックナンバーをチェックすることで判定することができます。
画像 URL の拡張子と画像データの形式が一致しない場合でも、画像データの先頭をチェックすることで、画像形式の判定を行うことができます。

jpg: 先頭が 0xff 0xd8
png: 先頭が 0x89 0x50(P) 0x43(N) 0x47(G)
gif: 先頭が 0x47(G) 0x49(I) 0x46(F)

ちなみに、gzip の場合は先頭が 0x1f 0x8b となります。

例えば、perl の場合には以下のようにして判定を行うことができます。
   if ($obj =~ /^\xff\xd8/) {
        print("jpg\n");
    }
    elsif ($obj =~ /^\x89\x50\x43\x47/) {
        print("png\n");
    }
    elsif ($obj =~ /^\x47\x49\x46/) {
        print("gif\n");
    }
    elsif ($obj =~ /^\x1f\x8b/) {
        print("gzip\n");
    }
    else {
        print("other\n");
    }