我有一个文件和目录列表。我正在尝试编写一个函数来删除条目,其中还存在一个祖先目录的条目。到目前为止我的工作似乎有效,但我认为这是低效的,因为它测试每个文件的完整目录列表。
也许有一个图书馆可以做到这一点,但我找不到它。目的是允许用户选择要上载的文件和目录列表。
从示例中可以看出,目录是条目的子集。我更愿意提供参赛作品。
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
感谢所有贡献的人!
答案 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)