2013-06-09 3 views
4

У меня есть список:Используйте список для фильтрации другого списка в Python

data_list = ['a.1','b.2','c.3'] 

И я хочу, чтобы получить только строки, которые начинаются со строками из другого списка:

test_list = ['a.','c.'] 

a.1 и c.3 должны быть вернулся.

Я полагаю, я мог бы использовать двойной для цикла:

for data in data_list: 
    for test in test_list: 
     if data.startswith(test): 
      # do something with item 

мне было интересно, если есть что-то более элегантное и, возможно, более peformant.

+0

Являются ли строки в test_list произвольной длины? – jh314

+0

@ jh314 да может быть любой длины. –

ответ

12

str.startswith может также принимать кортеж (но не список) префиксов:

test_tuple=tuple(test_list) 
for data in data_list: 
    if data.startswith(test_tuple): 
     ... 

что означает простой список понимание даст вам отфильтрованный список:

matching_strings = [ x for x in data_list if x.startswith(test_tuple) ] 

или вызов filter:

import operator 
f = operator.methodcaller('startswith', tuple(test_list)) 
matching_strings = filter(f, test_list) 
+1

Это круто! Я не знал, что 'startswith' может взять кортеж :) – Andbdrew

+0

Любая причина использовать' x for x' вместо 'lambda x:' или наоборот? –

+0

Ну, нет вызова использовать лямбда со списком. Но если вы спрашиваете об использовании понимания списка вместо 'filter' (который мог бы использовать лямбда вместо функции, возвращаемой' methodcaller'), тогда нет, нет особых причин использовать один над другим, я думаю , Я подозреваю, что каждая из них имеет схожую производительность, и я позволю другим спорить о том, что больше Pythonic :). – chepner

2

Попробуйте следующее:

for data in data_list: 
    if any(data.startswith(test) for test in test_list): 
     # do something 

any() является встроенным, которая принимает итератор и возвращает True первого значение из итератора, что Ие правда, в противном случае возвращает False. В моем примере я использую выражение генератора, а не строю список (который был бы расточительным).

+0

'any()' было бы хорошо, если бы все, что требовалось, было знать, было ли совпадение, но здесь нам нужно также вернуть совпадение; поэтому я поддержал ответ @ chepner. –

1

Отъезд filter и any в документах python.

>>> data_list = ['a.1','b.2','c.3'] 
>>> test_list = ['a.','c.'] 
>>> new_list = filter(lambda x: any(x.startswith(t) for t in test_list), data_list) 
>>> new_list 
['a.1', 'c.3'] 

После этого вы можете делать все, что вы хотите с материалом в вашем new_list.

Как @Chepner указывает, вы также можете поставить кортеж строк для startswith, так что выше, также может быть написано:

>>> data_list = ['a.1','b.2','c.3'] 
>>> test_tuple = ('a.','c.') 
>>> new_list = filter(lambda x: x.startswith(test_tuple), data_list) 
>>> new_list 
['a.1', 'c.3'] 
+0

Что-то не так с первым, оно ничего не вернуло. –

+0

@ AndyArismendi это работает здесь :) – Andbdrew

+0

sry, вы правы. Я просто запускал 'filter (...)' в PyScripter. Обычно я вижу вывод на консоли, но на этот раз он не показывал его, пока я не добавил 'print'. –

3

Просто используйте filter с lambda function и startswith:

data_list = ['a.1','b.2','c.3'] 
test_list = ('a.','c.') 

result = filter(lambda x: x.startswith(test_list), data_list) 

print(list(result)) 

Выходные:

['a.1', 'c.3'] 
1

В качестве альтернативы, вспыхивают регулярные выражения

import re 
# build a pattern that matches any of the strings we are interested in 
pattern = re.compile('|'.join(map(re.escape, test_list))) 
# filter by matches 
print filter(pattern.match, data_list) 

Это, вероятно, максимально возможно в C и может быть более эффективным, чем другие решения. Это может быть немного сложно для непосвященных, чтобы следовать, хотя.

+0

Если я изменю 'test_list' на' ['2', 'c.'] 'Я бы подумал, что это даст мне' b.2', а также 'c.3', но это только дает мне' c.3 '. Выведенное регулярное выражение - '2 | c \ .', поэтому я не знаю, почему он не возвратил' b.2'. –

+0

@AndyArismendi, соответствуют только совпадениям в начале строк. –

Смежные вопросы