2013-02-01 344 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

相關問題