如何根据元组的索引值从列表中删除重复的元组,同时保持元组的顺序? [复制]
Posted
技术标签:
【中文标题】如何根据元组的索引值从列表中删除重复的元组,同时保持元组的顺序? [复制]【英文标题】:How can I remove duplicate tuples from a list based on index value of tuple while maintaining the order of tuple? [duplicate] 【发布时间】:2018-09-27 13:24:17 【问题描述】:我想删除那些在索引 0 处具有相同值的元组,但第一次出现除外。我查看了其他类似的问题,但没有得到我正在寻找的特定答案。有人能帮帮我吗? 以下是我尝试过的。
from itertools import groupby
import random
Newlist = []
abc = [(1,2,3), (2,3,4), (1,0,3),(0,2,0), (2,4,5),(5,4,3), (0,4,1)]
Newlist = [random.choice(tuple(g)) for _, g in groupby(abc, key=lambda x: x[0])]
print Newlist
我的预期输出:[(1,2,3), (2,3,4), (0,2,0), (5,4,3)]
【问题讨论】:
有了random.choice
,你的尝试如何保证它只保留第一次出现?
【参考方案1】:
要正确使用groupby
,必须对序列进行排序:
>>> [next(g) for k,g in groupby(sorted(abc, key=lambda x:x[0]), key=lambda x:x[0])]
[(0, 2, 0), (1, 2, 3), (2, 3, 4), (5, 4, 3)]
或者如果您需要示例的非常精确的顺序(即保持原始顺序):
>>> [t[2:] for t in sorted([next(g) for k,g in groupby(sorted([(t[0], i)+t for i,t in enumerate(abc)]), lambda x:x[0])], key=lambda x:x[1])]
[(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
这里的诀窍是添加一个字段以在 groupby() 步骤之后保持原始顺序恢复。
编辑:甚至更短一点:
>>> [t[1:] for t in sorted([next(g)[1:] for k,g in groupby(sorted([(t[0], i)+t for i,t in enumerate(abc)]), lambda x:x[0])])]
[(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
【讨论】:
确实如此,但问题是关于维护元组的顺序。我认为没有使用groupby
的解决方案
@fferri:我需要元组的顺序,如预期输出所示。
@A.S 我更新了我的答案
@PatrickHaugh:任何工具都有解决方案=P
@fferri:感谢您付出的额外努力。现在可以了。【参考方案2】:
使用OrderedDict
的更好选择:
from collections import OrderedDict
abc = [(1,2,3), (2,3,4), (1,0,3), (0,2,0), (2,4,5),(5,4,3), (0,4,1)]
d = OrderedDict()
for t in abc:
d.setdefault(t[0], t)
abc_unique = list(d.values())
print(abc_unique)
输出:
[(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
简单但效率不高:
abc = [(1,2,3), (2,3,4), (1,0,3), (0,2,0), (2,4,5),(5,4,3), (0,4,1)]
abc_unique = [t for i, t in enumerate(abc) if not any(t[0] == p[0] for p in abc[:i])]
print(abc_unique)
输出:
[(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
【讨论】:
@jdehesa:谢谢,它有效。【参考方案3】:一种简单的方法是遍历列表并跟踪您已经找到的元素:
abc = [(1,2,3), (2,3,4), (1,0,3),(0,2,0), (2,4,5),(5,4,3), (0,4,1)]
found = set()
NewList = []
for a in abc:
if a[0] not in found:
NewList.append(a)
found.add(a[0])
print(NewList)
#[(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
found
是 set
。在每次迭代中,我们检查元组中的第一个元素是否已经在found
中。如果没有,我们将整个元组附加到NewList
。在每次迭代结束时,我们将元组的第一个元素添加到 found
。
【讨论】:
一个警告是,这仅在元组的第一个元素是可散列的情况下才有效(当然,如给定示例中的数字是)。 轻微改进:仅在found
中不存在的情况下将a[0]
添加到found
(即将found.add(a[0])
的缩进增加一级)。
如果a[0]
在found
中continue
会更好,否则追加/添加(如@JackTaylor 所述,没有缩进问题)
谢谢@pault:我在我的代码上使用了这个解决方案。【参考方案4】:
itertools recipes(Python 2:itertools recipes,但在这种情况下基本上没有区别)包含一个方法,它比@pault 的implementation 更通用一点。它还使用set
:
Python 2:
from itertools import ifilterfalse as filterfalse
Python 3:
from itertools import filterfalse
def unique_everseen(iterable, key=None): "List unique elements, preserving order. Remember all elements ever seen." # unique_everseen('AAAABBBCCDAABBB') --> A B C D # unique_everseen('ABBCcAD', str.lower) --> A B C D seen = set() seen_add = seen.add if key is None: for element in filterfalse(seen.__contains__, iterable): seen_add(element) yield element else: for element in iterable: k = key(element) if k not in seen: seen_add(k) yield element
使用它:
abc = [(1,2,3), (2,3,4), (1,0,3),(0,2,0), (2,4,5),(5,4,3), (0,4,1)]
Newlist = list(unique_everseen(abc, key=lambda x: x[0]))
print Newlist
# [(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
这应该稍微快一些,因为 set.add
方法的缓存(仅当您的 abc
很大时才真正相关)并且还应该更通用,因为它使 key
函数成为参数。
除此之外,我在评论中已经提到的相同限制适用:这仅适用于元组的第一个元素实际上是可散列的(当然,如给定示例中的数字是)。
【讨论】:
@A.S 已修复。在 Python 2 中,它被称为ifilterfalse
。它仍然有效,因为在这种情况下定义了 key
函数。【参考方案5】:
@PatrickHaugh 声称:
但问题显然是关于维护 元组。我认为没有使用 groupby 的解决方案
我从不错过 (ab) 使用 groupby()
的机会。这是我的无排序解决方案(一次或两次):
from itertools import groupby, chain
abc = [(1, 2, 3), (2, 3, 4), (1, 0, 3), (0, 2, 0), (2, 4, 5), (5, 4, 3), (0, 4, 1)]
Newlist = list((lambda s: chain.from_iterable(g for f, g in groupby(abc, lambda k: s.get(k[0]) != s.setdefault(k[0], True)) if f))())
print(Newlist)
输出
% python3 test.py
[(1, 2, 3), (2, 3, 4), (0, 2, 0), (5, 4, 3)]
%
【讨论】:
以上是关于如何根据元组的索引值从列表中删除重复的元组,同时保持元组的顺序? [复制]的主要内容,如果未能解决你的问题,请参考以下文章