2013-02-01 290 views
2

我在列表中遇到了一些问题。所以,基本上,我有一个列表:检查列表元素是否存在于另一个列表的元素中

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)的方式来实现这一目标?

我假设我可以循环检查每个元素,但我不确定这是否非常有效 - 我有一个大小约为50k的列表(b)。

回答

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'] 
+0

请注意前导和尾随空格。他希望在某些情况下(也许是所有情况下)修剪它。如果子字符串从'b'元素的中间切掉,他可能不需要额外的空格。 –

+0

@ sr2222 - 也许。这就像在'regex.sub'的末尾添加'.strip()一样简单,或者允许正则表达式匹配它们周围的空白 - ''|'.join(r'\ s * {} \ s *”。格式(重。escape(x))for x in a' – mgilson

+0

也许增加一些断字保护?否则“红色狗”也会与“转移的狗狗”相匹配。 – DSM

1

那么,最简​​单的将是一个直接的列表理解,只要a是小的,它甚至是一个非常有效的方法。

b = [i for i in b if i not in a] 
+0

但是,这并不能给你“真棒”来自“真棒布兰妮长矛” – mgilson

+0

哦,你实际上想要删除子字符级别的'a'元素,我没有注意到这一点。在整个事情被过滤的情况下,b的元素应该留空还是移除? –

2

好了,我不知道,知道,如果这样继续下去了算作Python的因为reduce得到了在python3流放到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的我想。

下面是一个处理transferred dogcatcher的情况。我借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为可读性命名函数。

+0

I'米不知道这实际上是否会快得多...... – mgilson

+0

至少'timeit'这么说:-) – sloth

+0

是的,我也在简单的测试中使用'timeit'。看起来,对于我正在使用的简单字符串,'a in b'(其中'a'不在'b'中)比'b.replace(a,'')快大约4倍。当然,如果'a'几乎总是在'b'中,最终会变得更慢,但这可能不是OP的情况。 – mgilson

相关问题