Come verificare se uno dei seguenti elementi è in un elenco?


220

Sto cercando di trovare un modo breve per vedere se uno dei seguenti elementi è in un elenco, ma il mio primo tentativo non funziona. Oltre a scrivere una funzione per raggiungere questo obiettivo, è il modo più breve per verificare se uno di più elementi è in un elenco.

>>> a = [2,3,4]
>>> print (1 or 2) in a
False
>>> print (2 or 1) in a
True

Cosa divertente, ho controllato come 'e' si 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')è vero falso falso vero
Piotr Kamoda

Risposte:


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

Sia gli elenchi vuoti che gli insiemi vuoti sono False, quindi puoi utilizzare il valore direttamente come valore di verità.


6
L'idea di intersezione mi ha dato questa idea. return len (set (a) .intersezione (set (b)))
Deon

13
FWIW - Ho fatto un confronto di velocità e la prima soluzione offerta qui è stata di gran lunga il digiuno.
jackiekazil,

2
La risposta di @ user89788 usando un generatore è di nuovo molto più veloce, perché anypuò tornare presto non appena trova un Truevalore - non deve prima costruire l'intero elenco
Anentropico

La soluzione secondo / sets non funzionerà se nella lista sono presenti duplicati (poiché i set contengono solo uno di ciascun elemento). Se `L1 = [1,1,2,3] 'e' L2 = [1,2,3] ', tutti gli elementi verranno visti intersecarsi.
donrondadon,

so che ha quasi 10 anni, ma la prima soluzione non sembra funzionare per me. ho sostituito i numeri in L2 per stringhe e sto ricevendo il seguente errore: TypeError: 'in <string>' richiede stringa come operando sinistro, non elenco
roastbeeef

227

Ah, Tobias, mi hai battuto. Stavo pensando a questa leggera variazione sulla tua soluzione:

>>> a = [1,2,3,4]
>>> b = [2,7]
>>> print(any(x in a for x in b))
True

5
Mi rendo conto che questa è una risposta molto vecchia, ma se un elenco è molto lungo e l'altro è breve, esiste un ordine che produrrebbe prestazioni più veloci? (ie, x in long for x in shortvs x in short for x in long)
Luke Sapan,

11
@ LukeSapan: hai ragione. Tale ordine può essere ottenuto tramite "stampa qualsiasi (x in max (a, b, chiave = len) per x in min (a, b, chiave = len))". Questo usa x in breve per x in breve.
Nuclearman,

2
Questa è la risposta migliore perché utilizza un generatore e tornerà non appena viene trovata una corrispondenza (come hanno già detto altri, non solo su questa risposta!).
dotcomly

4
@Nuclearman, attenzione: se le due liste ae bsono della stessa lunghezza, max e min restituirà il più a sinistra l'elenco, il che rende la any()chiamata operano nello stesso elenco su entrambi i lati. Se è assolutamente necessario il controllo per la lunghezza, invertire l'ordine delle liste in seconda convocazione: any(x in max(a, b, key=len) for x in (b, a, key=len)).
Noah Bogart,

3
@NoahBogart Hai ragione e quella soluzione sembra buona come una qualsiasi. Presumo anche che volevi dire: any(x in max(a, b, key=len) for x in min(b, a, key=len))(mancato il minuto).
Nuclearman,

29

Forse un po 'più pigro:

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

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

1
È quasi uguale a quello che ho pubblicato.
Bastien Léonard,

5
@ BastienLéonard ... tranne che è molto più veloce perché usa un generatore e quindi anypuò tornare presto, mentre la tua versione deve costruire l'intero elenco dalla comprensione prima di anypoterlo usare. La risposta di @utente89788 è leggermente migliore perché le doppie parentesi non sono necessarie
Anentropico

17

Pensa a cosa dice effettivamente il codice!

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

Ciò dovrebbe probabilmente spiegarlo. :) Python apparentemente implementa "pigro o", il che non dovrebbe sorprendere. Lo esegue in questo modo:

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

Nel primo esempio, x == 1e y == 2. Nel secondo esempio, è viceversa. Ecco perché restituisce valori diversi a seconda dell'ordine di essi.


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

Versione codice golf. Prendi in considerazione l'utilizzo di un set se ha senso farlo. Lo trovo più leggibile di una comprensione dell'elenco.


12

1 riga senza comprensione dell'elenco.

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


6

In Python 3 possiamo iniziare a utilizzare l'asterisco di decompressione. Dati due elenchi:

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

Modifica: incorpora il suggerimento di alkanen


1
@Anthony, crea un set contenente gli elementi in a e un altro set contenente gli elementi in b, quindi trova l'intersezione (elementi condivisi) tra tali set e any () restituisce vero se ci sono tali elementi che sono veri. La soluzione non funzionerà se gli unici elementi condivisi sono falsi (come il numero 0). Potrebbe essere meglio usare len () di qualunque ()
alkanen

1
@alkanen Buona chiamata
Daniel Braun,

perché non usare la funzione set?
Alex78191,

5

Quando pensi "controlla per vedere se a in b", pensa agli hash (in questo caso, imposta). Il modo più veloce è eseguire l'hashing dell'elenco che si desidera controllare, quindi controllare ogni elemento presente.

Ecco perché la risposta di Joe Koberg è veloce: il controllo dell'intersezione impostata è molto veloce.

Tuttavia, quando non si dispone di molti dati, creare set può essere una perdita di tempo. Quindi, puoi creare un set dell'elenco e controllare ogni elemento:

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 il numero di elementi che si desidera controllare è piccolo, la differenza può essere trascurabile. Ma controlla un sacco di numeri contro un grande elenco ...

test:

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)

velocità:

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

Il metodo che è costantemente veloce è quello di creare un set (dell'elenco), ma l'intersezione funziona su set di dati di grandi dimensioni al meglio!


3

In alcuni casi (ad esempio elementi di elenco univoci), è possibile utilizzare le operazioni impostate.

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

Oppure, usando set.isdisjoint () ,

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

2

Questo lo farà in una riga.

>>> a=[2,3,4]
>>> b=[1,2]
>>> bool(sum(map(lambda x: x in b, a)))
True

Non sto ottenendo un True qui >>> stampa a [2, 3, 4] >>> stampa b [2, 7] >>> riduci (lambda x, y: x in b, a) False
Deon

Sì. Hai ragione. riduci () non gestiva abbastanza i valori booleani come pensavo. La versione rivista che ho scritto sopra funziona per quel caso però.
Chris Upchurch,

2

Ho raccolto alcune delle soluzioni menzionate in altre risposte e nei commenti, quindi ho eseguito un test di velocità. not set(a).isdisjoint(b)si è rivelato essere il più veloce, inoltre non ha rallentato molto quando il risultato è statoFalse .

Ognuna delle tre prove verifica un piccolo campione delle possibili configurazioni di ae b. I tempi sono in microsecondi.

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

Devo dire che la mia situazione potrebbe non essere quella che stai cercando, ma potrebbe fornire un'alternativa al tuo pensiero.

Ho provato sia il metodo set () che any () ma ho ancora problemi con la velocità. Quindi mi sono ricordato che Raymond Hettinger ha detto che tutto in Python è un dizionario e usa dict ogni volta che puoi. Quindi è quello che ho provato.

Ho usato un defaultdict con int per indicare risultati negativi e ho usato l'elemento nel primo elenco come chiave per il secondo elenco (convertito in defaultdict). Poiché hai una ricerca istantanea con dict, sai immediatamente se quell'elemento esiste nel defaultdict. So che non riesci sempre a cambiare la struttura dei dati per il tuo secondo elenco, ma se sei in grado di farlo dall'inizio, è molto più veloce. Potrebbe essere necessario convertire list2 (elenco più grande) in un valore predefinito, dove key è il valore potenziale che si desidera controllare da un piccolo elenco e il valore è 1 (hit) o ​​0 (no hit, default).

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

-4

Semplice.

_new_list = []
for item in a:
    if item in b:
        _new_list.append(item)
    else:
        pass

1
Questo non risponde alla domanda. OP vuole sapere se un valore dall'elenco a è nell'elenco b.
That1 Acquista
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.