Python:从2个列表中删除重复项

18

我想从两个列表中删除重复项,于是我编写了这个函数:

a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]

b = ["ijk", "lmn", "opq", "rst", "123", "456", ]

for i in b:
    if i in a:
        print "found " + i
        b.remove(i)

print b

但我发现,匹配项后面的项并没有被删除。

我得到的结果如下:

found ijk
found opq
['lmn', 'rst', '123', '456']

但我希望的结果是这样的:

['123', '456']

我该如何修复我的函数来实现我想要的结果?

谢谢。

11个回答

38

您的问题似乎是您正在更改正在进行迭代的列表。请改为对列表的副本进行迭代。

for i in b[:]:
    if i in a:
        b.remove(i)


>>> b
['123', '456']

不过,使用列表推导式怎么样?

>>> a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]
>>> b = ["ijk", "lmn", "opq", "rst", "123", "456", ]
>>> [elem for elem in b if elem not in a ]
['123', '456']

如果列表a变得更长,将其转换为集合set可能会更有效率(对于集合,x in s的时间复杂度是O(1),而对于列表则是O(n)),参考http://wiki.python.org/moin/TimeComplexity。 - Frerich Raabe

38

以下是正在发生的事情。假设您有以下列表:

['a', 'b', 'c', 'd']

您正在遍历列表中的每个元素。假设您当前位于索引位置1:

['a', 'b', 'c', 'd']
       ^
       |
   index = 1

如果您删除索引位置为1的元素,将得到如下结果:

['a',      'c', 'd']
       ^
       |
    index 1

在移除该项后,其他项向左滑动,呈现如下:
['a', 'c', 'd']
       ^
       |
    index 1

然后当循环再次运行时,循环将索引增加到2,得到以下结果:

['a', 'c', 'd']
            ^ 
            |
         index = 2

你看,你跳过了 'c' 这个元素。这个教训是:不要从正在遍历的列表中删除元素。


28

那么呢?

b= set(b) - set(a)

如果您需要在结果中重复出现可能重复的b内容并且需要保持其顺序,请使用:
b= [ x for x in b if not x in a ] 

会做的。

2
这个回答被踩了一次。有人能告诉我为什么吗?是否存在严重的语法/概念错误?是否没有对所问问题做出贡献(并考虑到有时很难理解所问问题)?是否英语太差以至于无法理解? - Mario Rossi
我看到了 if not x in a,对我来说有点奇怪。虽然它也能正常工作,但我认为你应该改成 if x not in a,这样代码会更清晰。这是我的个人意见。 - Lê Tư Thành
请注意,此处必须编写列表推导式选项:[x for x in d if not (x in o)],以通过pep8。 - Rob

6

您要求删除两个列表中的重复项,这是我的解决方案:

from collections import OrderedDict
a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]
b = ["ijk", "lmn", "opq", "rst", "123", "456", ]

x = OrderedDict.fromkeys(a)
y = OrderedDict.fromkeys(b)

for k in x:
    if k in y:
        x.pop(k)
        y.pop(k)


print x.keys()
print y.keys()

结果:

['abc', 'def', 'xyz']
['123', '456']

这里的好处是您可以保留两个列表项的顺序。

3
或者一个集合
set(b).difference(a)

请注意,如果顺序很重要的话,集合将无法保留顺序。


3

您可以使用Lambda函数。

f = lambda list1, list2: list(filter(lambda element: element not in list2, list1))

从list1中删除list2中的重复元素。

>>> a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]
>>> b = ["ijk", "lmn", "opq", "rst", "123", "456"]
>>> f(a, b)
['abc', 'def', 'xyz']
>>> f(b, a)
['123', '456']

2
避免在迭代列表时出现编辑问题的一种方法是使用推导式:
a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]
b = ["ijk", "lmn", "opq", "rst", "123", "456", ]
b = [x for x in b if not x in a]

Mario Rossi和Sukrit Kalra在1小时前发布了相同的解决方案。 - DevLounge
也许@Mayur Patel和我同时开始写它。这是一个元主题(我猜):当1(或者也许2)个人正在回答问题时,要么阻止提问(一段时间内?),要么至少指示还有多少其他人在回答。我的意思是在答案发布之前。虽然我是新手。如果已经有类似的功能,请告诉我。 - Mario Rossi

1
你可以使用列表推导式。
a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]
b = ["ijk", "lmn", "opq", "rst", "123", "456", ]

a中重复的值已被移除

c=[value for value in a if value not in b]

从 b 中删除了重复值

c=[value for value in b if value not in a]

0
a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]

b = ["ijk", "lmn", "opq", "rst", "123", "456","abc"]

for i in a:
    if i in b:
        print("found", i)
        b.remove(i)
print(b)

output:
found abc
found ijk
found lmn
found opq
found rst
['123', '456']


1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

0

已经有很多关于“如何修复它”的答案了,所以这是一个“如何改进它并更具Python风格的解决方法”:由于你想要实现的是获取列表 b 和列表 a 之间的差异,你应该在集合上使用差异操作符( 集合操作):

>>> a = ["abc", "def", "ijk", "lmn", "opq", "rst", "xyz"]
>>> b = ["ijk", "lmn", "opq", "rst", "123", "456", ]
>>> s1 = set(a)
>>> s2 = set(b)
>>> s2 - s1
set(['123', '456'])

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