繁体   English   中英

根据另一个不完整值的较短列表对列表进行排序

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM