删除重复项的最有效方法

时间:2017-10-09 22:36:13

标签: python-3.x

我有一个日志文件,我需要从中删除重复的条目。文件中的每一行由逗号分隔的三个部分组成,我们分别称它们为A,B和C.

当且仅当他们的A和C相等时,两个条目是重复的。如果找到重复项,则保留最大B的那个。

真实日志文件有大量行,以下仅作为简化示例:

日志文件(输入):

hostA, 1507300700.0, xyz
hostB, 1507300700.0, abc
hostB, 1507300800.0, xyz
hostA, 1507300800.0, xyz
hostA, 1507300900.0, xyz

删除(输出)重复项后的日志文件:

hostB, 1507300700.0, abc
hostB, 1507300800.0, xyz
hostA, 1507300900.0, xyz

我尝试将文件读入两个列表,然后按照以下方式进行比较:

for i in full_log_list_a:
    for j in full_log_list_b:
        if i[0] == j[0] and i[2] == j[2] and i[1] > j[1]:
            print(', '.join(i[0]), file=open(new_file, 'a'))

我还尝试过其他一些东西,但无论我做什么,它最终会在列表上迭代太多次并创建一堆重复条目,或者它无法找到具有最大B的项目。我知道可能有一个明显的答案,但我被困住了。有人可以指出我正确的方向吗?

1 个答案:

答案 0 :(得分:2)

我认为你要找的是dict而不是列表。

当您读取日志文件时,您将向dict添加条目,其中每个条目由一个键(A,C)和一个值B组成。如果一个键已经存在,则将B与映射到该键的值进行比较,并在必要时重新映射密钥(即,如果B大于当前映射到密钥的值)。

示例(对变量a,b和c使用更好的名称):

log_file_entries = {}

with open(log_file, 'r') as f:
    for line in f:
        a, b_str, c = line.split(', ')
        b = int(b_str)

        if (a, c) in log_file_entries:
            if b < log_file_entries[(a, c)]:
                continue

        log_file_entries[(a, c)] = b

这是一个循环。由于对dicts的所需操作(通常)在时间上是恒定的,即O(1),因此整体time complexity将为O(n),远优于嵌套循环的O(n²)时间复杂度。< / p>

当你稍后重写文件时,你可以像这样循环遍历dict:

with open(new_file, 'a') as f:
    for (a, c), b in log_file_entries.items():
        print('{0}, {1}, {2}'.format(a, b, c), file=f)

如果任何代码或术语不正确,我会道歉。我暂时没有触及Python。

(P.S。在您的示例代码中,您使用两个列表,而您可以在两个循环中使用相同的列表。)

<强>更新

如果您希望键的值包含日志文件中某一行的每个部分,您可以像这样重写上面的代码:

log_file_entries = {}

with open(log_file, 'r') as f:
    for line in f:
        a, b_str, c = line.split(', ')
        b = int(b_str)

        if (a, c) in log_file_entries:
            if b < log_file_entries[(a, c)][1]:
                continue

        log_file_entries[(a, c)] = (a, b, c)

with open(new_file, 'a') as f:
    for entry in log_file_entries.values():
        print(', '.join(entry), file=f)