Как проверить, есть ли один из следующих пунктов в списке?

220

Я пытаюсь найти краткий способ узнать, есть ли в списке какой-либо из следующих пунктов, но моя первая попытка не работает. Помимо написания функции для этого, есть любой короткий способ проверить, есть ли один из нескольких элементов в списке.

>>> a = [2,3,4]
>>> print (1 or 2) in a
False
>>> print (2 or 1) in a
True
Деон
источник
Забавно, я проверил как и как себя ведет. a = [1, 2] b = [3, 5, 2, 6, 8, 9] c = [3, 5, 6, 8, 1, 9] print( (1 and 2) in b ,(2 and 1) in b ,(1 and 2) in c ,(2 and 1) in c, sep='\n')это правда ложь ложь правда
Петр Камода

Ответы:

268
>>> L1 = [2,3,4]
>>> L2 = [1,2]
>>> [i for i in L1 if i in L2]
[2]


>>> S1 = set(L1)
>>> S2 = set(L2)
>>> S1.intersection(S2)
set([2])

И пустые списки, и пустые наборы имеют значение False, поэтому вы можете напрямую использовать это значение в качестве истинного значения.

Джо Коберг
источник
6
Идея пересечения дала мне эту идею. return len (set (a) .intersection (set (b)))
Деон
14
FWIW - я сделал сравнение скорости, и самое первое решение, предложенное здесь, было намного быстрее.
Джекиказил
2
Ответ @ user89788 с использованием генератора снова намного быстрее, потому что он anyможет вернуться рано, как только найдет Trueзначение - ему не нужно сначала создавать весь список
Anentropic
Второе решение / sets не будет работать, если у вас есть дубликаты в списке (так как наборы содержат только один из каждого элемента). Если `L1 = [1,1,2,3]" и "L2 = [1,2,3]", все элементы будут рассматриваться как пересекающиеся.
Донрондадон
я знаю, что это почти 10 лет, но первое решение, похоже, не работает для меня. я заменил числа в L2 на строки, и я получаю следующую ошибку: TypeError: 'in <string>' требует строку как левый операнд, а не как список
roastbeeef
227

Ах, Тобиас, ты побил меня этим. Я думал об этом небольшом изменении вашего решения:

>>> a = [1,2,3,4]
>>> b = [2,7]
>>> print(any(x in a for x in b))
True
ojdo
источник
5
Я понимаю, что это очень старый ответ, но если один список очень длинный, а другой короткий, есть ли порядок, который даст более высокую производительность? (т.е. x in long for x in shortпротив x in short for x in long)
Люк Сапан
11
@ LukeSapan: Вы правы. Этот порядок можно получить с помощью «напечатать любой (x в max (a, b, key = len) для x в min (a, b, key = len))». Это использует х в длинную для х в короткой.
Атомщик
2
Это лучший ответ, потому что он использует генератор и вернется, как только совпадение будет найдено (как уже говорили другие, только не в этом ответе!).
dotcomly
4
@Nuclearman, берегись: Если эти два списка aи bимеют одинаковую длину, максимальный и минимальный вернёт самый левый список, что делает any()вызов работать над тем же списком с обеих сторон. Если вы абсолютно требуют проверки длины в обратном порядке списков во втором вызове: any(x in max(a, b, key=len) for x in (b, a, key=len)).
Ноа Богарт
3
@ NoahBogart Вы правы, и это решение кажется таким же хорошим, как и любое. Я также предполагаю, что вы имели в виду: any(x in max(a, b, key=len) for x in min(b, a, key=len))(пропустил мин).
Атомщик
29

Может быть, немного более ленивым

a = [1,2,3,4]
b = [2,7]

print any((True for x in a if x in b))

источник
1
Это почти так же, как тот, который я опубликовал.
Бастьен Леонар
5
@ BastienLéonard ... за исключением того, что он намного быстрее, потому что он использует генератор и, следовательно, anyможет вернуться рано, тогда как ваша версия должна составить весь список из понимания, прежде чем его anyможно будет использовать. @ user89788 ответ немного лучше, потому что двойные скобки не нужны
Anentropic
17

Подумайте о том, что на самом деле говорит код!

>>> (1 or 2)
1
>>> (2 or 1)
2

Это должно объяснить это. :) По-видимому, Python реализует «ленивый или», что неудивительно. Он выполняет это примерно так:

def or(x, y):
    if x: return x
    if y: return y
    return False

В первом примере x == 1и y == 2. Во втором примере все наоборот. Вот почему он возвращает разные значения в зависимости от их порядка.

Дениз Доган
источник
17
a = {2,3,4}
if {1,2} & a:
    pass

Код гольф версии. Подумайте об использовании набора, если это имеет смысл. Я считаю это более читабельным, чем понимание списка.

00500005
источник
12

1 строка без списочных представлений.

>>> any(map(lambda each: each in [2,3,4], [1,2]))
True
>>> any(map(lambda each: each in [2,3,4], [1,5]))
False
>>> any(map(lambda each: each in [2,3,4], [2,4]))
True
Химел дас
источник
7

Лучшее, что я мог придумать:

any([True for e in (1, 2) if e in a])
Бастьен Леонар
источник
6

В Python 3 мы можем начать использовать распакованную звездочку. Дано два списка:

bool(len({*a} & {*b}))

Изменить: включить предложение Алканена

Даниэль Браун
источник
1
@Anthony, он создает набор, содержащий элементы в a, и другой набор, содержащий элементы в b, затем он находит пересечение (общие элементы) между этими наборами, и any () возвращает true, если есть такие элементы, которые являются правдивыми. Решение не будет работать, если единственные общие элементы являются ложными (например, число 0). Возможно, лучше использовать len (), чем any ()
alkanen
1
@alkanen Хороший звонок
Даниэль Браун
почему бы не использовать функцию set?
Alex78191
5

Когда вы думаете «проверить, если a в b», подумайте о хешах (в данном случае, устанавливает). Самый быстрый способ - хэшировать список, который вы хотите проверить, а затем проверять каждый элемент в нем.

Вот почему ответ Джо Коберга быстрый: проверка пересечения множества выполняется очень быстро.

Когда у вас мало данных, создание наборов может быть пустой тратой времени. Итак, вы можете составить список и просто проверить каждый элемент:

tocheck = [1,2] # items to check
a = [2,3,4] # the list

a = set(a) # convert to set (O(len(a)))
print [i for i in tocheck if i in a] # check items (O(len(tocheck)))

Когда количество предметов, которые вы хотите проверить, мало, разница может быть незначительной. Но сравните множество чисел с большим списком ...

Тесты:

from timeit import timeit

methods = ['''tocheck = [1,2] # items to check
a = [2,3,4] # the list
a = set(a) # convert to set (O(n))
[i for i in tocheck if i in a] # check items (O(m))''',

'''L1 = [2,3,4]
L2 = [1,2]
[i for i in L1 if i in L2]''',

'''S1 = set([2,3,4])
S2 = set([1,2])
S1.intersection(S2)''',

'''a = [1,2]
b = [2,3,4]
any(x in a for x in b)''']

for method in methods:
    print timeit(method, number=10000)

print

methods = ['''tocheck = range(200,300) # items to check
a = range(2, 10000) # the list
a = set(a) # convert to set (O(n))
[i for i in tocheck if i in a] # check items (O(m))''',

'''L1 = range(2, 10000)
L2 = range(200,300)
[i for i in L1 if i in L2]''',

'''S1 = set(range(2, 10000))
S2 = set(range(200,300))
S1.intersection(S2)''',

'''a = range(200,300)
b = range(2, 10000)
any(x in a for x in b)''']

for method in methods:
    print timeit(method, number=1000)

скорость:

M1: 0.0170331001282 # make one set
M2: 0.0164539813995 # list comprehension
M3: 0.0286040306091 # set intersection
M4: 0.0305438041687 # any

M1: 0.49850320816 # make one set
M2: 25.2735087872 # list comprehension
M3: 0.466138124466 # set intersection
M4: 0.668627977371 # any

Непрерывно быстрый метод состоит в создании одного набора (из списка), но пересечение работает с большими наборами данных лучше всего!

dantiston
источник
3

В некоторых случаях (например, уникальные элементы списка) могут использоваться операции над множествами.

>>> a=[2,3,4]
>>> set(a) - set([2,3]) != set(a)
True
>>> 

Или, используя set.isdisjoint () ,

>>> not set(a).isdisjoint(set([2,3]))
True
>>> not set(a).isdisjoint(set([5,6]))
False
>>> 
гимель
источник
2

Это будет сделано в одну строку.

>>> a=[2,3,4]
>>> b=[1,2]
>>> bool(sum(map(lambda x: x in b, a)))
True
Крис Упчерч
источник
Я не получаю True здесь >>> print a [2, 3, 4] >>> print b [2, 7] >>> lower (лямбда x, y: x в b, a) False
Deon
Ага. Ты прав. Reduce () не совсем обрабатывал логические значения так, как я думал. Пересмотренная версия, которую я написал выше, работает в этом случае.
Крис Апчерч
2

Я собрал несколько решений, упомянутых в других ответах и ​​комментариях, а затем провел тест скорости. not set(a).isdisjoint(b)Оказалось, что он был самым быстрым, но когда результат был, он не сильно замедлился False.

Каждый из трех прогонов тестирует небольшой образец возможных конфигураций aи b. Время в микросекундах.

Any with generator and max
        2.093 1.997 7.879
Any with generator
        0.907 0.692 2.337
Any with list
        1.294 1.452 2.137
True in list
        1.219 1.348 2.148
Set with &
        1.364 1.749 1.412
Set intersection explcit set(b)
        1.424 1.787 1.517
Set intersection implicit set(b)
        0.964 1.298 0.976
Set isdisjoint explicit set(b)
        1.062 1.094 1.241
Set isdisjoint implicit set(b)
        0.622 0.621 0.753

import timeit

def printtimes(t):
    print '{:.3f}'.format(t/10.0),

setup1 = 'a = range(10); b = range(9,15)'
setup2 = 'a = range(10); b = range(10)'
setup3 = 'a = range(10); b = range(10,20)'

print 'Any with generator and max\n\t',
printtimes(timeit.Timer('any(x in max(a,b,key=len) for x in min(b,a,key=len))',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('any(x in max(a,b,key=len) for x in min(b,a,key=len))',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('any(x in max(a,b,key=len) for x in min(b,a,key=len))',setup=setup3).timeit(10000000))
print

print 'Any with generator\n\t',
printtimes(timeit.Timer('any(i in a for i in b)',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('any(i in a for i in b)',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('any(i in a for i in b)',setup=setup3).timeit(10000000))
print

print 'Any with list\n\t',
printtimes(timeit.Timer('any([i in a for i in b])',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('any([i in a for i in b])',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('any([i in a for i in b])',setup=setup3).timeit(10000000))
print

print 'True in list\n\t',
printtimes(timeit.Timer('True in [i in a for i in b]',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('True in [i in a for i in b]',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('True in [i in a for i in b]',setup=setup3).timeit(10000000))
print

print 'Set with &\n\t',
printtimes(timeit.Timer('bool(set(a) & set(b))',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('bool(set(a) & set(b))',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('bool(set(a) & set(b))',setup=setup3).timeit(10000000))
print

print 'Set intersection explcit set(b)\n\t',
printtimes(timeit.Timer('bool(set(a).intersection(set(b)))',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('bool(set(a).intersection(set(b)))',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('bool(set(a).intersection(set(b)))',setup=setup3).timeit(10000000))
print

print 'Set intersection implicit set(b)\n\t',
printtimes(timeit.Timer('bool(set(a).intersection(b))',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('bool(set(a).intersection(b))',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('bool(set(a).intersection(b))',setup=setup3).timeit(10000000))
print

print 'Set isdisjoint explicit set(b)\n\t',
printtimes(timeit.Timer('not set(a).isdisjoint(set(b))',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('not set(a).isdisjoint(set(b))',setup=setup2).timeit(10000000))
printtimes(timeit.Timer('not set(a).isdisjoint(set(b))',setup=setup3).timeit(10000000))
print

print 'Set isdisjoint implicit set(b)\n\t',
printtimes(timeit.Timer('not set(a).isdisjoint(b)',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('not set(a).isdisjoint(b)',setup=setup1).timeit(10000000))
printtimes(timeit.Timer('not set(a).isdisjoint(b)',setup=setup3).timeit(10000000))
print
жевать носки
источник
0

Я должен сказать, что моя ситуация может быть не той, которую вы ищете, но она может предоставить альтернативу вашему мышлению.

Я пробовал как set (), так и any () метод, но все еще есть проблемы со скоростью. Итак, я вспомнил, что Рэймонд Хеттингер сказал, что все в python - это словарь и используй dict, когда можешь. Вот что я пытался.

Я использовал defaultdict с int для обозначения отрицательных результатов и использовал элемент в первом списке в качестве ключа для второго списка (преобразованного в defaultdict). Поскольку у вас есть мгновенный поиск с помощью dict, вы сразу же знаете, существует ли этот элемент в defaultdict. Я знаю, что вы не всегда можете изменить структуру данных для вашего второго списка, но если вы можете сделать это с самого начала, то это происходит намного быстрее. Возможно, вам придется преобразовать list2 (больший список) в defaultdict, где ключ - это потенциальное значение, которое вы хотите проверить из небольшого списка, а значение равно 1 (попадание) или 0 (без попадания, по умолчанию).

from collections import defaultdict
already_indexed = defaultdict(int)

def check_exist(small_list, default_list):
    for item in small_list:
        if default_list[item] == 1:
            return True
    return False

if check_exist(small_list, already_indexed):
    continue
else:
    for x in small_list:
        already_indexed[x] = 1
yangliu2
источник
-4

Просто.

_new_list = []
for item in a:
    if item in b:
        _new_list.append(item)
    else:
        pass
PyGuy
источник
1
Это не отвечает на вопрос. ОП хочет знать, есть ли какое-либо значение из списка a в списке b.
That1Guy