在列表中查找并更新重复项

2 投票
3 回答
814 浏览
提问于 2025-04-16 21:46

我在寻找一种更符合Python风格的方法来解决以下问题。我有一个(我认为是)可行的解决方案,但它的流程控制太复杂,看起来不太“好看”。(基本上,这是一个C++的解决方案)

我有一个列表的列表。每个列表里包含多个不同类型的项目(每个列表可能有大约10个项目)。这些列表的整体顺序并不重要,但每个列表内部项目的顺序是很重要的。(也就是说,我不能改变它)。

我想通过在每个列表的末尾添加一个额外的字段来“标记”重复项。然而,在这种情况下,“重复”列表是指在几个预选字段中有相同值的列表,但并不是所有字段都有相同的值(没有“真正的”重复项)。

举个例子:如果这是一个包含5个项目的列表的原始数据,并且重复的定义是第一个和第三个字段的值相等:

['apple', 'window', 'pear', 2, 1.55, 'banana']
['apple', 'orange', 'kiwi', 3, 1.80, 'banana']
['apple', 'envelope', 'star_fruit', 2, 1.55, 'banana']
['apple', 'orange', 'pear', 2, 0.80, 'coffee_cup'] 
['apple', 'orange', 'pear', 2, 3.80, 'coffee_cup']

那么第一个、第四个和第五个列表将被视为重复,因此所有列表应该更新如下:

['apple', 'window', 'pear', 2, 1.55, 'banana', 1]
['apple', 'orange', 'kiwi', 3, 1.55, 'banana', 0]
['apple', 'envelope', 'star_fruit', 2, 1.55,'banana', 0]
['apple', 'orange', 'pear', 2, 3.80, 'coffee_cup', 2]  
['apple', 'orange', 'pear', 2, 3.80, 'coffee_cup', 3]

感谢任何帮助或指导。我觉得这可能超出了《学习Python》这本书的范围。

3 个回答

0

这是我的解决方案(带注释的代码):

import itertools

l = [
        ['apple', 'window', 'pear', 2, 1.55, 'banana'],
        ['apple', 'orange', 'kiwi', 3, 1.80, 'banana'],
        ['apple', 'envelope', 'star_fruit', 2, 1.55, 'banana'],
        ['apple', 'orange', 'pear', 2, 0.80, 'coffee_cup'],
        ['apple', 'orange', 'pear', 2, 3.80, 'coffee_cup']
    ]

#Here you can select the important fields 
key = lambda i: (i[0],i[2])

l.sort(key=key)
grp = itertools.groupby(l, key=key)
#Look at itertools documentation
grouped = (list(j) for i,j in grp)

for i in grouped:
    if len(i) == 1:
        i[0].append(0)
    else: #You want duplicates to start from 1
        for pos, item in enumerate(i, 1):
            item.append(pos)

#Just a little loop for flattening the list
result = [] 
for i in grouped:
    for j in i:
        result.append(j)

print(result)

输出结果:

[['apple', 'orange', 'kiwi', 3, 1.8, 'banana', 0],
 ['apple', 'window', 'pear', 2, 1.55, 'banana', 1],
 ['apple', 'orange', 'pear', 2, 0.8, 'coffee_cup', 2],
 ['apple', 'orange', 'pear', 2, 3.8, 'coffee_cup', 3],
 ['apple', 'envelope', 'star_fruit', 2, 1.55, 'banana', 0]]
1

你最好的办法是先对列表进行排序,使用 itemgetter() 来选择需要匹配的字段作为 key。这样做会让所有匹配的字段放在一起,方便比较和标记。例如,如果你想匹配第一和第三个字段,可以这样排序:

lst.sort(key=itemgetter(0, 2))

接下来,比较每个项目和它前面的那个就很简单了。

好了,这里是完整的解决方案(使用了 itemgetter 和 groupby):

from operator import itemgetter
from itertools import groupby

def tagdups(input_seq, tag, key_indexes):
    keygetter = itemgetter(*key_indexes)
    sorted_list = sorted(input_seq, key=keygetter)
    for key, group in groupby(sorted_list, keygetter):
        group_list = list(group)
        if len(group_list) <= 1:
            continue
        for item in group_list:
            item.append(tag)
    return sorted_list

下面是一个示例测试运行,展示如何使用:

>>> samp = [[1,2,3,4,5], [1,3,5,7,7],[1,4,3,5,8],[4,3,2,7,5],[1,6,3,7,4]]
>>> tagdups(samp, 'dup', (0,2))
[[1, 2, 3, 4, 5, 'dup'], [1, 4, 3, 5, 8, 'dup'], [1, 6, 3, 7, 4, 'dup'], [1, 3, 5, 7, 7], [4, 3, 2, 7, 5]]
3
from collections import defaultdict

lists = [['apple', 'window', 'pear', 2, 1.55, 'banana'],
['apple', 'orange', 'kiwi', 3, 1.80, 'banana'],
['apple', 'envelope', 'star_fruit', 2, 1.55, 'banana'],
['apple', 'orange', 'pear', 2, 0.80, 'coffee_cup'],
['apple', 'orange', 'pear', 2, 3.80, 'coffee_cup']]

dic = defaultdict(int)
fts = []
for lst in lists:
    first_third = lst[0], lst[2]
    dic[first_third] += 1
    if dic[first_third] == 2: fts.append(first_third)
    lst.append(dic[first_third])

for lst in lists:
    if (lst[0], lst[2]) not in fts:
        lst[-1] -= 1

print(lists)

编辑:感谢utdemir。first_third = lst[0], lst[2] 是正确的,不是 first_third = lst[0] + lst[2]

编辑2:为了更清楚,改了变量名。

编辑3:修改成反映原发帖者真正想要的内容,以及他更新后的列表。现在看起来不太好,想要的更改只是简单加上去的。

撰写回答