如何在Python中比较两个列表并返回匹配项

530

我想要取两个列表中共同出现的值。

a = [1, 2, 3, 4, 5]
b = [9, 8, 7, 6, 5]

returnMatches(a, b)

比如说,会返回[5]


7
以下所有回答都对我来说似乎是错误的。如果一个数字在任何一个列表中重复了,那么会发生什么,你肯定想知道这一点吧?(例如,假设两个列表都有两个'5')。使用集合的任何解决方案都会立即删除所有重复项,这样你就会失去那些信息。 - M.H.
这个问题被解释成了两种不同的方式。如果目标是找到两个列表中所有共同的元素(无论它们在列表中出现的位置如何),那么这就是一个列表交集。否则,如果目标是比较对应位置上的每一对元素,那么我们只需逐对迭代并检查每一对即可。无论哪种方式,都有更好的版本的问题,所以我用两个不同的重复链接关闭了它。 - Karl Knechtel
如果想要返回不在另一个数据框中的列(也适用于列表),可以使用numpy解决方案,这里是jezrael的答案。https://dev59.com/W6Dia4cB1Zd3GeqPCFi3#43029056 - DOT
21个回答

685

虽然不是最有效的方法,但迄今为止最明显的方法是:

>>> a = [1, 2, 3, 4, 5]
>>> b = [9, 8, 7, 6, 5]
>>> set(a) & set(b)
{5}

如果顺序很重要,你可以使用列表推导式来实现:

>>> [i for i, j in zip(a, b) if i == j]
[5]

(仅适用于相同大小的列表,因为顺序的重要性意味着这一点)。


26
注意,列表推导式并不一定是更快的选项。对于较大的集合(性能最重要的情况),按位比较(&)或者set(a).intersection(b)可能会和列表推导式一样快或者更快。 - Joshmaker
40
另外需要注意的是:列表推导式会在相同的位置上找到出现的值(这也是SilentGhost所说的“顺序很重要”的意思)。集合交集方法也可以在不同的位置上找到匹配项。这是对两个不同问题的回答...(原帖的问题模棱两可,不太清楚它到底在问哪一个问题)。 - drevicko
1
如果你的列表是列表的列表,例如a = [[0,0], [1,0]]和b = [[2,3],[0,0]],你该如何操作? - Schneems
4
第一个示例 set(a) & set(b) 的时间复杂度是什么? - AdjunctProfessorFalcon
2
你如何查找在列表A中但不在列表B中的项目? - oldboy
显示剩余2条评论

560

36
这个答案的算法性能很好,因为只有其中一个列表(较短的应该被优先选择)被转换成集合进行快速查找,而另一个列表则被遍历,在集合中查找其元素。 - u0b34a0f6ae
32
bool(set(a).intersection(b)) 的意思是判断集合a和b是否有交集,如果有返回True,否则返回False。 - Akshay
10
这个答案更加灵活易读,因为人们可能需要使用differenceunion方法。这两个方法的详细说明可以在Python官方文档的frozenset.differencefrozenset.union中找到。 - Shihe Zhang
如果我的列表元素是对象,并且我只想进行部分匹配,即只有一些属性需要匹配才能被视为匹配的对象,该怎么办? - CGFoX
1
.intersection()&之间有性能差异吗? - brandonbanks
显示剩余2条评论

138

快速性能测试表明Lutz的解决方案是最好的:

import time

def speed_test(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        for x in xrange(5000):
            results = func(*args, **kwargs)
        t2 = time.time()
        print '%s took %0.3f ms' % (func.func_name, (t2-t1)*1000.0)
        return results
    return wrapper

@speed_test
def compare_bitwise(x, y):
    set_x = frozenset(x)
    set_y = frozenset(y)
    return set_x & set_y

@speed_test
def compare_listcomp(x, y):
    return [i for i, j in zip(x, y) if i == j]

@speed_test
def compare_intersect(x, y):
    return frozenset(x).intersection(y)

# Comparing short lists
a = [1, 2, 3, 4, 5]
b = [9, 8, 7, 6, 5]
compare_bitwise(a, b)
compare_listcomp(a, b)
compare_intersect(a, b)

# Comparing longer lists
import random
a = random.sample(xrange(100000), 10000)
b = random.sample(xrange(100000), 10000)
compare_bitwise(a, b)
compare_listcomp(a, b)
compare_intersect(a, b)

这是在我的电脑上得到的结果:

# Short list:
compare_bitwise took 10.145 ms
compare_listcomp took 11.157 ms
compare_intersect took 7.461 ms

# Long list:
compare_bitwise took 11203.709 ms
compare_listcomp took 17361.736 ms
compare_intersect took 6833.768 ms

显然,任何人为的性能测试都应该持保留态度,但由于set().intersection()的答案至少和其他解决方案一样快,并且也是最易读的,它应该成为这个常见问题的标准解决方案。


集合实际上是在去除重复项,所以在我的情况下不起作用。 - rgralma
@rgralma 从现有的list创建一个新的set不会从原始的list中删除任何内容。如果您想要处理列表中的重复项的特殊逻辑,我认为您需要提出一个新问题,因为答案将需要根据您希望如何处理重复项而具体指定。 - Joshmaker

89

我更喜欢基于集合的答案,但这里有一个仍然可以工作的答案。

[x for x in a if x in b]

29

一种快速的方法:

list(set(a).intersection(set(b)))

23

最简单的方法是使用集合

>>> a = [1, 2, 3, 4, 5]
>>> b = [9, 8, 7, 6, 5]
>>> set(a) & set(b)
set([5])

17

你也可以尝试这个方法,将共同的元素保留在一个新列表中。

new_list = []
for element in a:
    if element in b:
        new_list.append(element)

我实际上非常喜欢这个答案,对我来说它是最易读的,特别适合初学者,尤其是在处理较小的数据集时。 :) - Myexgiko

17
>>> s = ['a','b','c']   
>>> f = ['a','b','d','c']  
>>> ss= set(s)  
>>> fs =set(f)  
>>> print ss.intersection(fs)   
   **set(['a', 'c', 'b'])**  
>>> print ss.union(fs)        
   **set(['a', 'c', 'b', 'd'])**  
>>> print ss.union(fs)  - ss.intersection(fs)   
   **set(['d'])**

1
被接受的答案对于包含字符串的列表无效,而这个可以。 - HelloWorld101

13

你需要重复吗?如果不需要,也许你应该使用集合:

>>> set([1, 2, 3, 4, 5]).intersection(set([9, 8, 7, 6, 5]))
set([5])

如果你真的想要列表, http://www.java2s.com/Code/Python/List/Functiontointersecttwolists.htm
intersect([1, 2, 3, 4, 5], [9, 8, 7, 6, 5]) [5]
- Timothy Pratley
根据文档 - ...避免出错的构造,如Set('abc') & 'cbs',而更倾向于更易读的Set('abc').intersection('cbs')。 - http://docs.python.org/library/sets.html - Aaron Newton

11

检查列表1(lst1)和列表2(lst2)的相等性的另一种更加功能性的方法,其中对象深度为1,并且保持顺序的方法是:

all(i == j for i, j in zip(lst1, lst2))   

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