かえるのプログラミングブログ

プログラミングでつまずいたところとその解決策などを書いていきます。

自然数の順番になっているリストが欲しい

こんばんは、かえるるるです。

今回は、

os.listdir() などで

['file11.txt', 'file2.txt', 'file4.txt', 'file5.txt', 'file8.txt', 'file3.txt', 'file1.txt', 'file13.txt', 'file14.txt', 'file6.txt', 'file10.txt', 'file12.txt', 'file9.txt', 'file7.txt', 'file0.txt']

のようになって返ってきたファイルたちを、

['file0.txt', 'file1.txt', 'file2.txt', 'file3.txt', 'file4.txt', 'file5.txt', 'file6.txt', 'file7.txt', 'file8.txt', 'file9.txt', 'file10.txt', 'file11.txt', 'file12.txt', 'file13.txt', 'file14.txt']

のように自然数の順番で使用したい場面に出くわして、少しだけ困ったのでその解決策を記しておこうと思った次第です。

0. 準備

['file11.txt', 'file2.txt', 'file4.txt', 'file5.txt', 'file8.txt', 'file3.txt', 'file1.txt', 'file13.txt', 'file14.txt', 'file6.txt', 'file10.txt', 'file12.txt', 'file9.txt', 'file7.txt', 'file0.txt']

このようなリストを擬似的に作成するコードはこちらです。

import random
import re
from natsort import natsorted

lst = [str(i) for i in range(15)]

random.shuffle(lst)  # 順番をバラバラにする。

file_list = [f'file{i}.txt' for i in lst]

print(file_list)

1. sorted(list)

まず、無秩序に並んでいるリストに一定の秩序を与えたい時、 sorted() 関数の使用を思いつくと思います。

print(sorted(file_list))

['file0.txt', 'file1.txt', 'file10.txt', 'file11.txt', 'file12.txt', 'file13.txt', 'file14.txt', 'file2.txt', 'file3.txt', 'file4.txt', 'file5.txt', 'file6.txt', 'file7.txt', 'file8.txt', 'file9.txt']

このように、0, 1, 10, 11, ..., 2, 20, 21, ... というような秩序だってはいますがこれじゃねえんだよ。。というリストが返ってきます。

2. sorted(list, key=function)

SNS で呟くと、sorted には key を渡せまっせ。とアドバイスをいただきました。ありがとうございます。

key にはどこを参照して並び替えたいのかを返す関数を渡せば良いみたいです。

'file' と '.txt' を除いた、数字の部分を参照して欲しいので以下のような helper 関数を作成しました。

def return_check_item(x):
    x = re.sub(f'^{"file"}', '', x)
    x = re.sub(f'{".txt"}$', '', x)
    return int(x)

そして、

print(sorted(file_list, key=return_check_item))

['file0.txt', 'file1.txt', 'file2.txt', 'file3.txt', 'file4.txt', 'file5.txt', 'file6.txt', 'file7.txt', 'file8.txt', 'file9.txt', 'file10.txt', 'file11.txt', 'file12.txt', 'file13.txt', 'file14.txt']

欲しい順番になりました。

3. natsorted(list)

sorted にhelper 関数を渡さなくてもよしなにしてくれる関数がありました。

pip3 install natsort=='5.5.0'


from natsort import natsorted

print(natsorted(file_list))

['file0.txt', 'file1.txt', 'file2.txt', 'file3.txt', 'file4.txt', 'file5.txt', 'file6.txt', 'file7.txt', 'file8.txt', 'file9.txt', 'file10.txt', 'file11.txt', 'file12.txt', 'file13.txt', 'file14.txt']

実務利用であれば、helper 関数を書いた方がいい気がしますが覚えておいて損はないでしょう。

4. 0 padding

SNS で呟いたら 0 埋めしたらええやんとアドバイスをいただきました。 やってみましょう。

['file11.txt', 'file2.txt', 'file4.txt', 'file5.txt', 'file8.txt', 'file3.txt', 'file1.txt', 'file13.txt', 'file14.txt', 'file6.txt', 'file10.txt', 'file12.txt', 'file9.txt', 'file7.txt', 'file0.txt']

このように名前がすでについているものの使用を想定しているので、rename 関数を用意します。 ファイル名文字列を file 数字 .txt に分割し、数字のところに zfill 関数で任意の桁数になるまで 0で埋めたものを代入します。

def rename(x):
    prefix = "file"
    suffix = ".txt"

    x = re.sub(f'^{prefix}', '', x)
    x = re.sub(f'{suffix}$', '', x)
    
    return prefix+f"{str(x).zfill(5)}"+suffix
padded_lst = [rename(i) for i in file_list]
padded_lst

['file00007.txt',
 'file00000.txt',
 'file00014.txt',
 'file00006.txt',
 'file00009.txt',
 'file00010.txt',
 'file00013.txt',
 'file00011.txt',
 'file00012.txt',
 'file00005.txt',
 'file00004.txt',
 'file00008.txt',
 'file00003.txt',
 'file00001.txt',
 'file00002.txt']
sorted(padded_lst)

['file00000.txt',
 'file00001.txt',
 'file00002.txt',
 'file00003.txt',
 'file00004.txt',
 'file00005.txt',
 'file00006.txt',
 'file00007.txt',
 'file00008.txt',
 'file00009.txt',
 'file00010.txt',
 'file00011.txt',
 'file00012.txt',
 'file00013.txt',
 'file00014.txt']

簡単でした。

まとめ

リストについて少し詳しくなった。 SNS すごい。 ありがとうございました。

参考にしたサイトなど

[python] sorted のkeyってなんぞ - Qiita

OS - [python] os.listdir()の出力を数字順にsortして、それらを順番にpd.read_csvで読み込みたい|teratail

Pythonで文字列・数値をゼロ埋め(ゼロパディング) | note.nkmk.me