まえがき
競プロで使うけど、イマイチよくわかってなかったので。
よりよい実装はたくさん転がってる気がしますが。
2023/02/13
付記
コメントにて「sortedでできる」とありました。
こっちのほうが便利そうです。
persons = [
["Eve", 19],
["Adam", 19],
["Mike", 52],
["Anderson", 21],
["Mike", 53]
]
for person in sorted(persons, key=lambda person: (person[0], -person[1])):
print(*person)
sorted
関数のkey
にlambda式を渡しています。
キーをかっこで囲み、複数与えることで複数条件でも実装できますね。めっちゃ便利じゃん。
前提
以下のような入力データを想定します。
persons = [
["Eve", 19],
["Adam", 19],
["Mike", 52],
["Anderson", 21],
["Mike", 53]
]
ソート条件は以下の通り。上から順に適用します。
- 名前を辞書順に昇順ソート
- 年齢を降順ソート
辞書順ソートについては以下の通りとします。(要は辞書に並んでいる順です)
頭文字から順番に 「A」 から 「Z」 又は 「あ」 から「ん」 までを考え、若い文字(最初の文字)から順番に文字列を作る方法
今回はバブルソートを軸に実装していきます。
ここでは詳しく扱いませんが、参考になりそうなページを付記しておきます。
実装
完成品を先に置いておきます。
def my_sort(l):
n = len(l)
for _ in range(n):
flag = False
for i in range(n-1):
# 名前比較(昇順)
if l[i][0] > l[i+1][0]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
continue
elif l[i][0] < l[i+1][0]:
continue
# 年齢比較(降順)
if l[i][1] < l[i+1][1]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
if not flag: break
return l
順を追って説明していきます。まずは標準的なバブルソートから。
# n回までで打ち切り(無限ループ回避)
for _ in range(n):
flag = False
for i in range(n-1):
# ↓↓↓条件↓↓↓
if l[i] > l[i+1]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
# 次のループを予約
flag = True
# フラグのチェック
if not flag: break
2つ目のfor文以降が条件文になっているのがわかります。
ここに適宜条件を実装していく形となります。
まずは名前比較。
Pythonには標準で文字列比較が用意されています(ありがたい)
# 名前比較(昇順)
if l[i][0] > l[i+1][0]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
# フラグ更新
flag = True
continue
elif l[i][0] < l[i+1][0]:
continue
この場合、辞書順で昇順になる単語がより小さいということになります(たとえばAdam
< Eve
)
次に、年齢の降順で並べ替えます。
同名が二人以上いる場合、年齢がより大きいほうが上ということになります。
# 年齢比較(降順)
if l[i][1] < l[i+1][1]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
最後に、フラグチェックのための一文を追加しておきます。
if not flag: break
さて、条件部分を見返してみます。
# 名前比較(昇順)
if l[i][0] > l[i+1][0]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
continue
elif l[i][0] < l[i+1][0]:
continue
# 年齢比較(降順)
if l[i][1] < l[i+1][1]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
名前比較の部分でcontinue
を使って早めに返しているのに気づきましたか?
continue
を使わなかった場合は以下のようになります。
# 名前比較(昇順)
if l[i][0] > l[i+1][0]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
# 年齢比較(降順)
if l[i][0] == l[i+1][0] and l[i][1] < l[i+1][1]:
# 交換
w = l[i]
l[i] = l[i+1]
l[i+1] = w
flag = True
行数が減った代わりに、ふたつめの条件式が煩雑になりました。
条件がふたつであればこれだけで済むので好みによりますが、三つ以降は条件式が膨れ上がる原因になるので、早めに返す癖をつけたほうがよさそうだな~と思います。
出力
さて、最初のデータを実際にソートしてみます。
$ /bin/python3 test.py
Adam 19
Anderson 21
Eve 19
Mike 53
Mike 52
まず、上の二行に注目。
Adam 19
Anderson 21
頭文字が同じ名前については二文字目以降が勘案されているのが確認できます。
そして、4-5行目にも注目。
Mike 53
Mike 52
同名の二人についても、年齢の降順でソートされています。
おわりに
マサカリ、お待ちしています。