Como verificar se um dos seguintes itens está em uma lista?

220

Estou tentando encontrar uma maneira curta de ver se algum dos itens a seguir está em uma lista, mas minha primeira tentativa não funciona. Além de escrever uma função para fazer isso, é uma maneira curta de verificar se um dos vários itens está em uma lista.

>>> a = [2,3,4]
>>> print (1 or 2) in a
False
>>> print (2 or 1) in a
True
Deon
fonte
Engraçado, eu verifiquei como 'e' se comporta. 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')é verdadeiro falso falso verdadeiro #
Piotr Kamoda 2/16

Respostas:

266
>>> 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])

As listas vazias e os conjuntos vazios são Falso, portanto, você pode usar o valor diretamente como um valor verdadeiro.

Joe Koberg
fonte
6
A ideia do cruzamento me deu essa ideia. return len (conjunto (a) .intersection (conjunto (b)))
Deon
13
FWIW - Fiz uma comparação de velocidade e a primeira solução oferecida aqui foi o jejum de longe.
21712
2
@ do user89788 resposta usando um gerador é muito mais rápido de novo, porque anypode voltar mais cedo, assim que encontra um Truevalor - ele não tem que construir toda a lista primeira
Anentropic
A segunda solução / conjuntos não funcionará se você tiver duplicatas na lista (pois os conjuntos contêm apenas um de cada item). Se `L1 = [1,1,2,3] 'e' L2 = [1,2,3] ', todos os itens serão cruzados.
donrondadon
Eu sei que isso tem quase 10 anos, mas a primeira solução não parece funcionar para mim. eu tenho substituído os números em L2 para cordas, e eu estou recebendo o seguinte erro: TypeError: 'em <string>' requer string como operando à esquerda, não lista
roastbeeef
227

Ah, Tobias, você me venceu. Eu estava pensando nessa pequena variação na sua solução:

>>> a = [1,2,3,4]
>>> b = [2,7]
>>> print(any(x in a for x in b))
True
ojdo
fonte
5
Sei que essa é uma resposta muito antiga, mas se uma lista for muito longa e a outra curta, existe uma ordem que produziria um desempenho mais rápido? (ie x in long for x in shortvs x in short for x in long)
Luke Sapan
11
@LukeSapan: Você está correto. Essa ordem pode ser obtida via "imprima qualquer (x no máximo (a, b, tecla = len) para x em min (a, b, tecla = len))". Isso usa x em comprimento para x em resumo.
Nuclearman
2
Esta é a melhor resposta, pois usa um gerador e retornará assim que uma correspondência for encontrada (como outros disseram, mas não nesta resposta!).
dotcomly
4
@Nuclearman, cuidado: se as duas listas ae btêm o mesmo comprimento, máximo e mínimo irá retornar o mais à esquerda lista, o que torna a any()chamada operar sobre a mesma lista em ambos os lados. Se você absolutamente requerem a verificação de comprimento, inverter a ordem das listas na segunda chamada: any(x in max(a, b, key=len) for x in (b, a, key=len)).
Noah Bogart
3
@NoahBogart Você está correto e essa solução parece tão boa quanto qualquer outra. Eu também presumo que você quis dizer: any(x in max(a, b, key=len) for x in min(b, a, key=len))(perdeu o minuto).
Nuclearman 31/01
29

Talvez um pouco mais preguiçoso:

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

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

fonte
1
É quase o mesmo que eu postei.
Bastien Léonard
5
@ BastienLéonard ... exceto que é muito mais rápido porque usa um gerador e, portanto, anypode retornar mais cedo, enquanto sua versão precisa criar a lista inteira a partir da compreensão antes de anypoder usá-la. @ resposta de user89788 é um pouco melhor, porque os parênteses duplos são desnecessários
Anentropic
17

Pense no que o código realmente diz!

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

Isso provavelmente deve explicar isso. :) O Python aparentemente implementa "preguiçoso ou", o que não deve surpreender. Ele executa algo parecido com isto:

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

No primeiro exemplo, x == 1e y == 2. No segundo exemplo, é vice-versa. É por isso que retorna valores diferentes, dependendo da ordem deles.

Deniz Dogan
fonte
16
a = {2,3,4}
if {1,2} & a:
    pass

Código versão de golfe. Considere usar um conjunto, se fizer sentido. Acho isso mais legível do que uma compreensão de lista.

00500005
fonte
12

1 linha sem compreensão de lista.

>>> 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
Himel Das
fonte
7

O melhor que pude apresentar:

any([True for e in (1, 2) if e in a])
Bastien Léonard
fonte
6

No python 3, podemos começar a usar o asterisco descompactado. Dadas duas listas:

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

Edit: incorporar sugestão de alkanen

Daniel Braun
fonte
1
@Anthony, ele cria um conjunto contendo os elementos em a e outro conjunto contendo os elementos em b; em seguida, encontra a interseção (elementos compartilhados) entre esses conjuntos e any () retorna true se houver algum elemento verdadeiro. A solução não funcionará se os únicos elementos compartilhados forem falsos (como o número 0). Pode ser melhor usar len () do que qualquer ()
alkanen
1
@alkanen Boa chamada
Daniel Braun
por que não usar a função set?
Alex78191
5

Quando você pensa em "verificar se a em b", pense em hashes (nesse caso, define). A maneira mais rápida é fazer o hash da lista que você deseja verificar e, em seguida, verifique cada item lá.

É por isso que a resposta de Joe Koberg é rápida: verificar a interseção do conjunto é muito rápido.

Porém, quando você não possui muitos dados, criar conjuntos pode ser uma perda de tempo. Assim, você pode fazer um conjunto da lista e apenas verificar cada item:

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)))

Quando o número de itens que você deseja verificar é pequeno, a diferença pode ser insignificante. Mas verifique muitos números em uma lista grande ...

testes:

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)

velocidades:

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

O método que é consistentemente rápido é criar um conjunto (da lista), mas a interseção funciona melhor em grandes conjuntos de dados!

Dantiston
fonte
3

Em alguns casos (por exemplo, elementos de lista exclusivos), operações de conjunto podem ser usadas.

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

Ou, usando set.isdisjoint () ,

>>> not set(a).isdisjoint(set([2,3]))
True
>>> not set(a).isdisjoint(set([5,6]))
False
>>> 
gimel
fonte
2

Isso fará isso em uma linha.

>>> a=[2,3,4]
>>> b=[1,2]
>>> bool(sum(map(lambda x: x in b, a)))
True
Chris Upchurch
fonte
Não estou obtendo um True aqui >>> imprima a [2, 3, 4] >>> imprima b [2, 7] >>> reduza (lambda x, y: x em b, a) Falso
Deon
Sim. Você está certo. reduce () não estava lidando com valores booleanos da maneira que eu pensava. A versão revisada que escrevi acima funciona para esse caso.
21720 Chris Upchurch
2

Reuni várias das soluções mencionadas em outras respostas e comentários e, em seguida, executei um teste de velocidade. not set(a).isdisjoint(b)acabou sendo o mais rápido, também não diminuiu muito quando o resultado foiFalse .

Cada uma das três execuções testa uma pequena amostra das configurações possíveis de ae b. Os horários estão em microssegundos.

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
mastigar meias
fonte
0

Devo dizer que minha situação pode não ser o que você está procurando, mas pode fornecer uma alternativa ao seu pensamento.

Eu tentei o método set () e any (), mas ainda tenho problemas com a velocidade. Então, lembrei-me de Raymond Hettinger disse que tudo em python é um dicionário e use dict sempre que puder. Então foi isso que eu tentei.

Usei um defaultdict com int para indicar resultados negativos e usei o item na primeira lista como chave da segunda lista (convertida em defaultdict). Como você tem uma pesquisa instantânea com o dict, você sabe imediatamente se esse item existe no padrão. Sei que nem sempre é possível alterar a estrutura de dados para sua segunda lista, mas se você puder desde o início, será muito mais rápido. Pode ser necessário converter list2 (lista maior) em um padrão, onde key é o valor potencial que você deseja verificar na lista pequena e o valor é 1 (ocorrência) ou 0 (sem ocorrência, padrão).

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
fonte
-4

Simples.

_new_list = []
for item in a:
    if item in b:
        _new_list.append(item)
    else:
        pass
PyGuy
fonte
1
Isso não responde à pergunta. O OP deseja saber se algum valor da lista a está na lista b.
That1Guy