[英]Sorting a list based on another shorter list with incomplete values
我有一个文件路径列表,在读取和处理文件之前,需要以特定的方式对其进行排序。 具体方法由较小的列表定义,该列表仅包含一些文件名 ,但不是全部。 未在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
错误ValueError: 'bar_foo' is not in list
)或需要包含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)的方法来解决此问题吗?
我认为这是我会这样做的方式:
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
一个简单的实现是在排序之前在行中添加一些标记。 因此,不需要特定的排序。 如果所有文件名都遵循您提供的模式,则也可以避免使用正则表达式:
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']
让我想想。 这是一个独特的问题,我将尝试提出解决方案
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']
由于python的排序已经是稳定的,因此您只需要为它提供对sort键的粗略分组即可。
给定排序要求的细节,最好使用函数来完成。 例如:
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 key函数应使用它来产生分组键。 这是通过返回捕获预排序列表参数的函数(排序键)来实现的。
此sortkey()函数返回presorted_list中文件名的索引,或者返回不匹配文件名的索引号。 因此,如果在presorted_list中有2个名称,它们会将相应的文件归类为排序键值0和1。所有其他文件都将在组2中。
用于确定应在presorted_list中找到文件名的哪一部分的条件有些不清楚,因此,我仅介绍了括号中的特殊情况。 在sortkey()函数中,您可以添加更复杂的解析来满足您的需求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.