检查列表中的元素是否存在于另一个列表的元素中

2 投票
3 回答
857 浏览
提问于 2025-04-17 14:26

我在处理列表的时候遇到了一些问题。基本上,我有一个列表:

a=["Britney spears", "red dog", "\xa2xe3"]

还有另一个列表,看起来像这样:

b = ["cat","dog","red dog is stupid", "good stuff \xa2xe3", "awesome Britney spears"]

我想做的是检查列表 a 中的元素是否属于列表 b 中的某个元素。如果是的话,就把它们从 b 的元素中移除。所以,我希望 b 看起来像这样:

b = ["cat","dog","is stupid","good stuff","awesome"]

在 Python 2.7.x 中,最好的方法是什么呢?

我在想我可以通过循环来检查每个元素,但我不确定这样做是否高效,因为我的列表 b 大约有 5 万个元素。

3 个回答

1

最简单的方法就是直接用列表推导式,只要a的大小不大,这种方法其实还挺高效的。

b = [i for i in b if i not in a]
2

好吧,我不知道这算不算是现在的python风格,因为在python3中,reduce被移到了functools里。有人得给出一个一行代码的例子:

a = ["Britney spears", "red dog", "\xa2xe3"]
b = ["cat","dog","red dog is stupid", "good stuff \xa2xe3", "awesome Britney spears"]

b = [reduce(lambda acc, n: acc.replace(n, ''), a, x).strip() for x in b]

更快的写法是

[reduce(lambda acc, n: acc.replace(n, '') if n in acc else acc, a, x).strip() for x in b]

但是可读性下降了,我觉得就不那么python风格了。

这里有一个处理转移的捕狗者情况的例子。我借用了mgilson的正则表达式,但我觉得没问题,因为这很简单 :-):

def reducer(acc, n):
    if n in acc:
        return re.sub('(?:\s+|^)' + re.escape(n) + '(?:\s+|$)', '', acc)
    return acc

b = [reduce(reducer, a, x).strip() for x in b]

我把lambda提取成了一个命名函数,这样更容易阅读。

4

我觉得这里可以用正则表达式:

import re

a=["Britney spears", "red dog", "\xa2xe3"]

regex = re.compile('|'.join(re.escape(x) for x in a))

b=["cat","dog","red dog is stupid", "good stuff \xa2xe3", "awesome Britney spears"]

b = [regex.sub("",x) for x in b ]
print (b)  #['cat', 'dog', ' is stupid', 'good stuff ', 'awesome ']

这样,正则表达式引擎可以更好地优化对不同选项的测试。

这里有很多不同的选项,展示了不同的正则表达式是怎么工作的。

import re

a = ["Britney spears", "red dog", "\xa2xe3"]
b = ["cat","dog",
     "red dog is stupid", 
     "good stuff \xa2xe3", 
     "awesome Britney spears",
     "transferred dogcatcher"]

#This version leaves whitespace and will match between words.
regex = re.compile('|'.join(re.escape(x) for x in a))
c = [regex.sub("",x) for x in b ]
print (c) #['cat', 'dog', ' is stupid', 'good stuff ', 'awesome ', 'transfercatcher']

#This version strips whitespace from either end
# of the returned string
regex = re.compile('|'.join(r'\s*{}\s*'.format(re.escape(x)) for x in a))
c = [regex.sub("",x) for x in b ]
print (c) #['cat', 'dog', 'is stupid', 'good stuff', 'awesome', 'transfercatcher']

#This version will only match at word boundaries,
# but you lose the match with \xa2xe3 since it isn't a word
regex = re.compile('|'.join(r'\s*\b{}\b\s*'.format(re.escape(x)) for x in a))
c = [regex.sub("",x) for x in b ]
print (c) #['cat', 'dog', 'is stupid', 'good stuff \xa2xe3', 'awesome', 'transferred dogcatcher']


#This version finally seems to get it right.  It matches whitespace (or the start
# of the string) and then the "word" and then more whitespace (or the end of the 
# string).  It then replaces that match with nothing -- i.e. it removes the match 
# from the string.
regex = re.compile('|'.join(r'(?:\s+|^)'+re.escape(x)+r'(?:\s+|$)' for x in a))
c = [regex.sub("",x) for x in b ]
print (c) #['cat', 'dog', 'is stupid', 'good stuff', 'awesome', 'transferred dogcatcher']

撰写回答