从路径列表中删除冗余条目

时间:2018-03-25 16:54:10

标签: python algorithm path

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

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

从示例中可以看出,目录是条目的子集。我更愿意提供参赛作品。

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']

如果你知道一个库或者可以提供一个更好的算法的线索,我会很感激。同时,我会尝试基于对条目进行排序的东西,因为祖先应该始终在列表中的子项之前。

编辑: - 结果

我使用测试集通过探查器运行了10,000次所有解决方案 - 并添加了一个文件/home/fred/work/f2.txt.bak以测试确保常规文件名确实导致另一个被丢弃。

我的原始代码:1060004 function calls in 0.394 seconds

Stephen Rauch的回答 - 第一次工作:3250004 function calls in 2.089 seconds

carrdelling的答案 - 对于类似的文件名不起作用:480004 function calls in 0.146 seconds

carrdelling的编辑答案 - 适用于所有情况:680004 function calls in 0.231 seconds

感谢所有贡献的人!

2 个答案:

答案 0 :(得分:2)

如果您对输入的条目列表进行排序,那么问题就更容易了:

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 short-circuits(除非绝对必要,否则不会再对整个列表进行比较)。此外,由于列表已排序,因此可确保输出具有最小数量(和最高级别)路径。

编辑:

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

split_entries = sorted(entries, key=lambda x: (x.count(os.sep), -len(x)))

这样,树中较高的文件夹会提前(所以你最终会得到最少的路径数),但是在文件夹中,名字较长的文件会更早出现 - 所以它们不会被丢弃因为文件具有较短(类似前缀)的名称。

答案 1 :(得分:1)

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

代码:

def remove_redundant_entries(entries):
    present = set()
    result = []
    for entry in sorted(entries):
        path = os.path.abspath(entry).split(os.sep)
        found = any(
            tuple(path[:i+1]) in present for i in range(len(path)))
        if not found:
            result.append(entry)
            present.add(tuple(path))
    return result

测试代码:

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)