我有一个文件路径列表,在阅读和处理文件之前,我需要以特定方式订购。 特定方式由较小的列表定义,该列表仅包含一些文件名称,但不包含所有文件。 presorted_list
中未列出的所有其他文件路径都必须保持其先前的顺序。
实施例:
some_list = ['path/to/bar_foo.csv',
'path/to/foo_baz.csv',
'path/to/foo_bar(ignore_this).csv',
'path/to/foo(ignore_this).csv',
'other/path/to/foo_baz.csv']
presorted_list = ['foo_baz', 'foo']
expected_list = ['path/to/foo_baz.csv',
'other/path/to/foo_baz.csv',
'path/to/foo(ignore_this).csv',
'path/to/bar_foo.csv',
'path/to/foo_bar(ignore_this).csv']
我发现了一些相关帖子:
但据我所知,问题和答案总是依赖于两个相同长度的列表,这些列表我没有(导致ValueError: 'bar_foo' is not in list
之类的错误)或者需要的预先排序列表包含我无法提供的所有可能值。
我的想法:
我提出了一个似乎有用的解决方案,但我不确定这是否是解决问题的好方法:
import os
import re
EXCPECTED_LIST = ['path/to/foo_baz.csv',
'other/path/to/foo_baz.csv',
'path/to/foo(ignore_this).csv',
'path/to/bar_foo.csv',
'path/to/foo_bar(ignore_this).csv']
PRESORTED_LIST = ["foo_baz", "foo"]
def sort_function(item, len_list):
# strip path and unwanted parts
filename = re.sub(r"[\(\[].*?[\)\]]", "", os.path.basename(item)).split('.')[0]
if filename in PRESORTED_LIST:
return PRESORTED_LIST.index(filename)
return len_list
def main():
some_list = ['path/to/bar_foo.csv',
'path/to/foo_baz.csv',
'path/to/foo_bar(ignore_this).csv',
'path/to/foo(ignore_this).csv',
'other/path/to/foo_baz.csv',]
list_length = len(some_list)
sorted_list = sorted(some_list, key=lambda x: sort_function(x, list_length))
assert sorted_list == EXCPECTED_LIST
if __name__ == "__main__":
main()
是否还有其他(更短,更pythonic)方法来解决这个问题?
答案 0 :(得分:1)
以下是我认为我会这样做的方式:
import re
from collections import OrderedDict
from itertools import chain
some_list = ['path/to/bar_foo.csv',
'path/to/foo_baz.csv',
'path/to/foo_bar(ignore_this).csv',
'path/to/foo(ignore_this).csv',
'other/path/to/foo_baz.csv']
presorted_list = ['foo_baz', 'foo']
expected_list = ['path/to/foo_baz.csv',
'other/path/to/foo_baz.csv',
'path/to/foo(ignore_this).csv',
'path/to/bar_foo.csv',
'path/to/foo_bar(ignore_this).csv']
def my_sort(lst, presorted_list):
rgx = re.compile(r"^(.*/)?([^/(.]*)(\(.*\))?(\.[^.]*)?$")
d = OrderedDict((n, []) for n in presorted_list)
d[None] = []
for p in some_list:
m = rgx.match(p)
n = m.group(2) if m else None
if n not in d:
n = None
d[n].append(p)
return list(chain.from_iterable(d.values()))
print(my_sort(some_list, presorted_list) == expected_list)
# True
答案 1 :(得分:1)
一个简单的实现是在排序之前向线添加一些标记。所以不需要特定的订购。如果所有文件名都遵循您提供的模式,也可以避免使用正则表达式:
for n,file1 in enumerate(presorted_list):
for m,file2 in enumerate(some_list):
if '/'+file1+'.' in file2 or '/'+file1+'(' in file2:
some_list[m] = "%03d%03d:%s" % (n, m, file2)
some_list.sort()
some_list = [file.split(':',1)[-1] for file in some_list]
print(some_list)
结果:
['path/to/foo_baz.csv',
'other/path/to/foo_baz.csv',
'path/to/foo(ignore_this).csv',
'path/to/bar_foo.csv',
'path/to/foo_bar(ignore_this).csv']
答案 2 :(得分:0)
让我想一想。这是一个独特的问题,我将尝试提出解决方案
only_sorted_elements = filter(lambda x:x.rpartition("/")[-1].partition(".")[0] in presorted_list , some_list)
only_sorted_elements.sort(key = lambda x:presorted_list.index(x.rpartition("/")[-1].partition(".")[0]))
expected_list = []
count = 0
for ind, each_element in enumerate(some_list):
if each_element not in presorted_list:
expected_list.append(each_element)
else:
expected_list[ind].append(only_sorted_elements[count])
count += 1
希望这能解决您的问题。 我首先只过滤那些在presorted_list中的元素, 然后我按照presorted_list
中的顺序对这些元素进行排序然后我遍历列表并相应追加。
编辑:
将文件名中的索引参数更改为精确文件名。 这将保留那些不在预先排序列表中的文件的原始索引。
已编辑: 新编辑的代码将更改参数并首先提供排序结果,然后再排序。
some_list = ['path/to/bar_foo.csv',
'path/to/foo_baz.csv',
'path/to/foo_bar(ignore_this).csv',
'path/to/foo(ignore_this).csv',
'other/path/to/foo_baz.csv']
presorted_list = ['foo_baz', 'foo']
only_sorted_elements = filter(lambda x:x.rpartition("/")[-1].partition("(")[0].partition(".")[0] in presorted_list , some_list)
unsorted_all = filter(lambda x:x.rpartition("/")[-1].partition("(")[0].partition(".")[0] not in presorted_list , some_list)
only_sorted_elements.sort(key = lambda x:presorted_list.index(x.rpartition("/")[-1].partition("(")[0].partition(".")[0]))
expected_list = only_sorted_elements + unsorted_all
print expected_list
结果:
['path/to/foo_baz.csv',
'other/path/to/foo_baz.csv',
'path/to/foo(ignore_this).csv',
'path/to/bar_foo.csv',
'path/to/foo_bar(ignore_this).csv']
答案 3 :(得分:0)
由于python的排序已经稳定,您只需要为排序键提供粗略的分组。
鉴于您的排序要求的具体情况,使用函数可以做得更好。例如:
def presort(presorted):
def sortkey(name):
filename = name.split("/")[-1].split(".")[0].split("(")[0]
if filename in presorted:
return presorted.index(filename)
return len(presorted)
return sortkey
sorted_list = sorted(some_list,key=presort(['foo_baz', 'foo']))
为了保持流程的通用性和易用性,presorted_list应作为参数提供,sort键函数应使用它来生成分组键。这是通过返回一个捕获预先排序列表参数的函数(sortkey)来实现的。
此sortkey()函数返回presorted_list中文件名的索引或超出不匹配文件名的数字。因此,如果presorted_list中有2个名称,它们将在排序键值0和1下对相应的文件进行分组。所有其他文件将在组2中。
用于确定应该在presorted_list中找到文件名的哪个部分的条件有点不清楚,所以我只讨论了左括号的具体情况。在sortkey()函数中,您可以添加更复杂的解析来满足您的需求。