如何迭代遍历包含三个成对值的元组列表?

5

我希望删除列表中第一个元素相同的元组,因为我将字母对视为具有相同的值,而不考虑它们的顺序。这里是我正在尝试迭代的列表tuples2

[(3, 'A', 'C'), (3, 'C', 'A'), (2, 'B', 'C'), (2, 'C', 'B'), (1, 'A', 'B'), (1, 'B', 'A')]

我目前的代码:

for i in list(tuples2):
    if i[0] == i+1[0]:
        tuples2.remove(i)
    print tuples2

...正在抛出以下错误:

line 6: if i[0] == (i+1)[0]: TypeError: can only concatenate tuple (not "int") to tuple

如果我想得到 [(3, 'A', 'C'), (2, 'B', 'C'), (1, 'A', 'B')],我应该如何修改我的代码以考虑到这一点?

6个回答

4
这段代码存在很多问题。你不应该修改正在遍历的列表,否则会导致跳过某些项目。编辑我现在看到你在for循环中复制了这个列表,但是以下方法可能更加安全。你可以倒序迭代,但构建一个列表可能更简单。一种直接的方法是跟踪已经看到的第一个元素,并且仅添加如果之前没有看到过该元素:
In [1]: data = [(3, 'A', 'C'), (3, 'C', 'A'), (2, 'B', 'C'), (2, 'C', 'B'), (1, 'A', 'B'), (1, 'B', 'A')]
   ...:
In [2]: seen = set()

In [3]: new_data = []
   ...: for triple in data:
   ...:     first = triple[0]
   ...:     if first in seen:
   ...:         continue
   ...:     seen.add(first)
   ...:     new_data.append(triple)
   ...:

In [4]: new_data
Out[4]: [(3, 'A', 'C'), (2, 'B', 'C'), (1, 'A', 'B')]

使用.remove非常低效,它将您的算法从O(n)变为O(n ^ 2)。


非常感谢,这个操作非常简单,而且完美运行。我真的很感激你的帮助!以这种方式跟踪已经看到的元素正是我需要做的。 - Cora Coleman

3
你对 i in list(tuples2) 的迭代概念存在误解:使用这种语法,i 不是索引,而是元组本身。因此,你无法执行 i+1[0]
首先,我建议你执行:
tuples_list = list(tuples2)

为了解决这个问题,你可以使用Python建议的xrange(或者range)通过索引来实现:
for i in xrange(len(tuples_list)-1):
    if tuples_list[i][0] == tuples_list[i+1][0]:
        #Do what you want

谢谢,但是当我按照你的建议去做时,它会抛出“if tuples_list[i] == tuples_list[i+1]: IndexError: list index out of range”的错误。 - Cora Coleman
@CoraColeman 抱歉,正在修复 - 最后一次迭代当然会导致这个问题。 - Ofer Arial

3
你可以按照第一个组件作为键来读取字典中的值:
tuples = [(3, 'A', 'C'), (3, 'C', 'A'), (2, 'B', 'C'), (2, 'C', 'B'), (1, 'A', 'B'), (1, 'B', 'A')]
d = {x:(x,y,z) for x,y,z in tuples}
tuples = list(d.values())

最终的 tuples

[(1, 'B', 'A'), (2, 'C', 'B'), (3, 'C', 'A')]

非常感谢,这个也完美地解决了我的问题。不过,我希望能够保留列表的排序顺序,而 juanpa.arrivillaga 给出了这个答案。 - Cora Coleman

2
您可以使用 `itertools` 模块中的 `groupby` 方法来解决您的问题,如下所示:
from itertools import groubpy

a = [(3, 'A', 'C'), (3, 'C', 'A'), (2, 'B', 'C'), (2, 'C', 'B'), (1, 'A', 'B'), (1, 'B', 'A')]
final = [list(v)[0] for _,v in groupby(sorted(a), lambda x: x[0])]

print(final)

输出:

>>> [(1, 'A', 'B'), (2, 'B', 'C'), (3, 'A', 'C')]

否则,如果你需要与你在问题中给出的顺序相同的final列表,你可以将其反转:
final = list(reversed(final))
# OR
#final = sorted(final, reverse = True)
print(final)

输出:

>>> [(3, 'A', 'C'), (2, 'B', 'C'), (1, 'A', 'B')]

1
在这里使用 sorted 只是偶然可行的,并不具有普适性。 - juanpa.arrivillaga
我也可以使用 list(reversed(final)) - Chiheb Nexus
1
我不这么认为。我认为reversed只是偶然能够工作。当你在开始时对其进行排序时,你失去了顺序,因此如果原始列表没有按某种排序顺序排列,你将无法将其恢复。 - juanpa.arrivillaga

2
只需按第一个元素分组并取每个组的第一个即可。
>>> [next(g) for _, g in itertools.groupby(tuples2, lambda x: x[0])]
[(3, 'A', 'C'), (2, 'B', 'C'), (1, 'A', 'B')]

甚至更简单:
>>> tuples2[::2]
[(3, 'A', 'C'), (2, 'B', 'C'), (1, 'A', 'B')]

我认为在这种情况下,tuples[::2]是正确的方法。 - juanpa.arrivillaga

1

紧急问题

这句话是什么意思?

i+1[0]

i是一个元组;你正在尝试将i用作索引和元素。你需要的迭代更像是:

for i in range (len(tuples2)):
    if tuples2[i] == tuples2[i+1]:

...这仍然不能完成任务。这检查整个元组的相等性。但是,您说您只关心第一个元素的相等性。那么,您需要:

    if tuples2[i][0] == tuples2[i+1][0]:

这是针对你现有的代码;其他人已经展示了更多“Pythonic”的做法。
通用解决方案:
这段代码假设元组的其他元素相等,具有相同第一个元素的元组在列表中是相邻的,并且匹配的元组仅成对出现。你的列表可能包含类似以下内容的东西吗:
tuples2 = [(3, 'A', 'C'), (3, 'C', 'A'), 
           (2, 'B', 'C'), (2, 'C', 'B'), 
           (1, 'A', 'B'), (1, 'A', 'Z'), (1, 'B', 'A')]

如果额外的“Z”元素被埋在“3”元素之间,那么呢?无论如何,即使你对列表进行排序,你也会得到“AZ”元素位于其他“1”元素之间。

如果这对你来说是个问题,那么我建议你首先将每个元组转换为一个列表,将元素排序。例如,这将把(1,“B”,“A”)转换为[1,“A”,“B”]。然后使用任何给定的方法来消除重复项,包括你已经编程的方法。我通常通过将事物变回元组,然后形成一个集合来做到这一点——这自动消除了重复项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接