在列表中查找并更新重复项
我在寻找一种更符合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:修改成反映原发帖者真正想要的内容,以及他更新后的列表。现在看起来不太好,想要的更改只是简单加上去的。