Python 从路径列表中删除冗余项

Python 从路径列表中删除冗余项,python,algorithm,path,Python,Algorithm,Path,我有一个文件和目录的列表。我正试图编写一个函数来删除存在祖先目录项的条目。到目前为止,我所做的似乎是有效的,但我认为这是低效的,因为它测试每个文件的完整目录列表 也许有个图书馆可以做这个,但我找不到。其目的是允许用户选择要上载的文件和目录列表 从示例中可以看到,目录是条目的子集。我宁愿只提供条目 import os def remove_redundant_entries(entries, directories): result = [] for entry in entri

我有一个文件和目录的列表。我正试图编写一个函数来删除存在祖先目录项的条目。到目前为止,我所做的似乎是有效的,但我认为这是低效的,因为它测试每个文件的完整目录列表

也许有个图书馆可以做这个,但我找不到。其目的是允许用户选择要上载的文件和目录列表

从示例中可以看到,目录是条目的子集。我宁愿只提供条目

import os

def remove_redundant_entries(entries, directories):
    result = []
    for entry in entries:
        # make a copy and successively get the dirname and test it
        partial_path = entry
        found = False
        while partial_path != os.sep:
            partial_path = os.path.dirname(partial_path)
            if partial_path in directories:
                found = True
                break
        if not found:
            result.append(entry)
    return result


entries = [
    "/home/fred/work/f1.txt",
    "/home/fred/work/f2.txt",
    "/home/fred/play/f3.txt",
    "/home/fred/play",
    "/home/jane/dev/f1.txt",
    "/home/jane"]

directories = [
    "/home/fred/play",
    "/home/jane"]

print remove_redundant_entries(entries, directories)

# result:
['/home/fred/work/f1.txt', '/home/fred/work/f2.txt', '/home/fred/play', '/home/jane']
如果你知道一个库或者能提供一个更好的算法的线索,我会很感激。同时,我将尝试一些基于条目排序的方法,因为在列表中祖先应该总是在他们的孩子之前

编辑:-结果

我使用测试集在分析器中运行了10000次所有解决方案,并添加了一个文件
/home/fred/work/f2.txt.bak
,以测试确保一个常规文件名不会导致另一个文件名被丢弃

我的原始代码:
0.394秒内调用1060004个函数

斯蒂芬·劳赫的答案——第一次奏效:
3250004次函数调用,时间为2.089秒

carrdelling的答案——这对类似的文件名不起作用:
480004函数调用只需0.146秒

carrdelling编辑的答案-适用于所有情况:
680004函数调用只需0.231秒


感谢所有做出贡献的人

您可以使用集合更有效地查找已存在的内容,如:

代码: 测试代码:
如果对输入项列表进行排序,则问题更容易:

def remove_redundant_entries(entries):

    split_entries = sorted(entries)

    valid_entries = []

    for entry in split_entries:

        if any(entry.startswith(p) for p in valid_entries):
            continue
        valid_entries.append(entry)

    return valid_entries
请注意,
any
只要有一个比较为真(除非严格必要,否则不会对整个列表进行比较)。此外,由于列表已排序,因此可以保证输出将具有最少数量(和最高级别)的路径

编辑:

如果还需要在列表中保留同一文件夹中的多个文件(即使某些文件名是其他文件名的子集),则只需修改排序条件:

split_条目=排序(条目,key=lambda x:(x.count(os.sep),-len(x))


这样,树中较高的文件夹会出现得更早(因此最终路径数会最少),但在文件夹中,名称较长的文件会出现得更早,因此不会因为名称较短(类似前缀)的文件而被丢弃

使用字符串对象可用的
startswith
方法会更简单吗。这样,对于每个条目,您可以根据以任何目录字符串开头的条目来决定是否保留它?虽然算法并不简单,但它消除了while循环。我确实尝试过使用
startswith
,但可能会出现这样的情况:一个常规文件以另一个常规文件的名称开头:
/somepath/foo.txt.bak,/somepath/foo.txt
。话虽如此,regex也有可能做到这一点?你不需要regex-请看我的更新答案,如果你对输入列表进行了正确排序,那么你应该能够同时保留
/somepath/foo.txt.bak
/somepath/foo.txt
,即使使用
startswith
谢谢!我正要将其标记为接受,然后@carrdeling对他的答案进行了编辑,使其同样有效,但速度更快。是的,对长度进行二次排序可以解决类似文件名的问题。我把我的原始答案和所有答案都放在档案里。我将根据结果编辑问题。谢谢
import os

entries = [
    "/home/fred/work/f1.txt",
    "/home/fred/work/f2.txt",
    "/home/fred/play/f3.txt",
    "/home/fred/play",
    "/home/jane/dev/f1.txt",
    "/home/jane"]

result = remove_redundant_entries(entries)
expected = ['/home/fred/work/f1.txt', '/home/fred/work/f2.txt',
            '/home/fred/play', '/home/jane']
assert set(result) == set(expected)
def remove_redundant_entries(entries):

    split_entries = sorted(entries)

    valid_entries = []

    for entry in split_entries:

        if any(entry.startswith(p) for p in valid_entries):
            continue
        valid_entries.append(entry)

    return valid_entries