Jak sprawdzić, czy jeden z poniższych elementów znajduje się na liście?

220

Próbuję znaleźć krótki sposób, aby sprawdzić, czy którykolwiek z poniższych elementów znajduje się na liście, ale moja pierwsza próba nie działa. Oprócz napisania funkcji do osiągnięcia tego celu, jest to jakikolwiek krótki sposób sprawdzenia, czy jeden z wielu elementów znajduje się na liście.

>>> a = [2,3,4]
>>> print (1 or 2) in a
False
>>> print (2 or 1) in a
True
Deon
źródło
Zabawne, sprawdziłem, jak się zachowuje „i”. 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')jest prawdą Fałsz Fałsz prawda
Piotr Kamoda

Odpowiedzi:

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

Zarówno puste listy, jak i puste zestawy są fałszywe, więc możesz użyć wartości bezpośrednio jako wartości prawdy.

Joe Koberg
źródło
6
Pomysł skrzyżowania dał mi ten pomysł. return len (set (a). intersection (set (b)))
Deon
13
FWIW - Zrobiłem porównanie prędkości, a pierwszym zaproponowanym tutaj rozwiązaniem był post.
jackiekazil
2
Odpowiedź @ user89788 przy użyciu generatora jest znacznie szybsza, ponieważ anymoże wrócić wcześniej, jak tylko znajdzie Truewartość - nie musi najpierw budować całej listy
Anentropic
Drugie rozwiązanie / zestawy nie będzie działać, jeśli masz duplikaty na liście (ponieważ zestawy zawierają tylko jeden z każdego elementu). Jeśli „L1 = [1,1,2,3]” i „L2 = [1,2,3]”, wszystkie elementy będą się przecinać.
donrondadon,
wiem, że to prawie 10 lat, ale pierwsze rozwiązanie nie wydaje mi się skuteczne. zastąpiłem ciągi liczbami w L2 i otrzymuję następujący błąd: TypeError: 'in <string>' wymaga ciągu jako lewego operandu, a nie listy
roastbeeef
227

Ach, Tobiaszu, pobiłeś mnie do tego. Myślałem o tej niewielkiej odmianie twojego rozwiązania:

>>> a = [1,2,3,4]
>>> b = [2,7]
>>> print(any(x in a for x in b))
True
ojdo
źródło
5
Zdaję sobie sprawę, że to bardzo stara odpowiedź, ale jeśli jedna lista jest bardzo długa, a druga krótka, czy istnieje kolejność, która zapewniłaby większą wydajność? (tj. x in long for x in shortvs x in short for x in long)
Luke Sapan
11
@LukeSapan: Masz rację. Kolejność tę można uzyskać poprzez „wydrukuj dowolny (x w maks. (A, b, klucz = len) dla x w min (a, b, klucz = len))”. Używa x w skrócie dla x w skrócie.
Nuclearman
2
To najlepsza odpowiedź, ponieważ używa generatora i wróci, gdy tylko zostanie znalezione dopasowanie (jak powiedzieli inni, po prostu nie w tej odpowiedzi!).
dotcomly,
4
@Nuclearman, uważaj: jeśli dwie listy ai bmają taką samą długość, max i min powróci lewej najwięcej listę, co sprawia, że any()połączenia działają na tej samej liście po obu stronach. Jeśli koniecznie wymagają sprawdzania długości, odwrócić kolejność na listach w drugim naborze: any(x in max(a, b, key=len) for x in (b, a, key=len)).
Noah Bogart,
3
@NoahBogart Masz rację i to rozwiązanie wydaje się równie dobre jak każde inne. Zakładam również, że miałeś na myśli: any(x in max(a, b, key=len) for x in min(b, a, key=len))(spóźniłeś się na minutę).
Nuclearman
29

Może trochę bardziej leniwy:

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

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

źródło
1
Jest prawie taki sam jak ten, który opublikowałem.
Bastien Léonard,
5
@ BastienLéonard ... z wyjątkiem tego, że jest znacznie szybszy, ponieważ korzysta z generatora, dzięki czemu anymoże wrócić wcześniej, podczas gdy twoja wersja musi zbudować całą listę ze zrozumienia, zanim będzie anymogła z niej korzystać. Odpowiedź użytkownika @8989788 jest nieco lepsza, ponieważ podwójne nawiasy są niepotrzebne
Anentropic
17

Zastanów się, co tak naprawdę mówi kod!

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

To prawdopodobnie powinno to wyjaśniać. :) Python najwyraźniej implementuje „leniwe” lub „leniwe”, co nie powinno dziwić. Wykonuje to coś takiego:

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

W pierwszym przykładzie x == 1i y == 2. W drugim przykładzie jest odwrotnie. Dlatego zwraca różne wartości w zależności od ich kolejności.

Deniz Dogan
źródło
16
a = {2,3,4}
if {1,2} & a:
    pass

Kodowa wersja golfa. Rozważ użycie zestawu, jeśli ma to sens. Uważam to za bardziej czytelne niż zrozumienie listy.

00500005
źródło
12

1 linia bez objaśnień listy.

>>> 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
źródło
7

Najlepsze, co mogłem wymyślić:

any([True for e in (1, 2) if e in a])
Bastien Léonard
źródło
6

W Pythonie 3 możemy zacząć korzystać z gwiazdki rozpakowania. Biorąc pod uwagę dwie listy:

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

Edycja: uwzględnij sugestię alkanenu

Daniel Braun
źródło
1
@Anthony, tworzy zestaw zawierający elementy w a, i inny zestaw zawierający elementy wb, a następnie znajduje przecięcie (wspólne elementy) między tymi zestawami, a dowolny () zwraca true, jeśli istnieją takie elementy, które są prawdziwe. Rozwiązanie nie będzie działać, jeśli jedynymi współdzielonymi elementami są fałsz (np. Liczba 0). Lepszym rozwiązaniem może być użycie len () niż dowolnego ()
alkanen
1
@alkanen Dobra rozmowa
Daniel Braun
dlaczego nie użyć funkcji set?
Alex78191,
5

Kiedy myślisz „sprawdź, czy a in b”, pomyśl o hashach (w tym przypadku ustawia). Najszybszym sposobem jest haszowanie listy, którą chcesz sprawdzić, a następnie sprawdzenie każdej pozycji na liście.

Dlatego odpowiedź Joe Koberga jest szybka: sprawdzanie przecięcia zestawu jest bardzo szybkie.

Kiedy nie masz dużo danych, tworzenie zestawów może być stratą czasu. Możesz więc utworzyć zestaw listy i po prostu sprawdzić każdy element:

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

Gdy liczba elementów, które chcesz sprawdzić, jest niewielka, różnica może być nieznaczna. Ale sprawdź wiele liczb na dużej liście ...

testy:

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)

prędkości:

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

Metodą, która jest konsekwentnie szybka, jest utworzenie jednego zestawu (z listy), ale przecięcie działa najlepiej na dużych zestawach danych!

dantiston
źródło
3

W niektórych przypadkach (np. Unikalne elementy listy) można użyć operacji ustawiania.

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

Lub używając set.isdisjoint () ,

>>> not set(a).isdisjoint(set([2,3]))
True
>>> not set(a).isdisjoint(set([5,6]))
False
>>> 
gimel
źródło
2

To zrobi to w jednej linii.

>>> a=[2,3,4]
>>> b=[1,2]
>>> bool(sum(map(lambda x: x in b, a)))
True
Chris Upchurch
źródło
Nie otrzymuję tutaj Prawdy >>> wydrukuj [2, 3, 4] >>> wydrukuj b [2, 7] >>> zmniejsz (lambda x, y: xwb, a) Fałsz
Deon
Tak. Masz rację. redukcja () nie do końca obsługiwała wartości logiczne tak, jak myślałem. Jednak poprawiona wersja, którą napisałem powyżej, działa w tym przypadku.
Chris Upchurch,
2

Zebrałem kilka rozwiązań wymienionych w innych odpowiedziach i komentarzach, a następnie przeprowadziłem test prędkości. not set(a).isdisjoint(b)okazał się najszybszy, nie spowolnił też zbytnio, gdy wynik był False.

Każdy z trzech przebiegów testuje małą próbkę możliwych konfiguracji ai b. Czasy są w mikrosekundach.

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
żuć skarpetki
źródło
0

Muszę powiedzieć, że moja sytuacja może nie być tym, czego szukasz, ale może stanowić alternatywę dla twojego myślenia.

Próbowałem zarówno metody set (), jak i dowolnej (), ale nadal mam problemy z prędkością. Przypomniałem sobie, że Raymond Hettinger powiedział, że wszystko w pythonie jest słownikiem i używaj dict, kiedy tylko możesz. Więc tego próbowałem.

Użyłem defaultdict z int, aby wskazać negatywne wyniki i użyłem elementu z pierwszej listy jako klucza do drugiej listy (przekonwertowanej na defaultdict). Ponieważ masz natychmiastowe wyszukiwanie za pomocą dict, od razu wiesz, czy ten element istnieje w defaultdict. Wiem, że nie zawsze zmieniasz strukturę danych na drugiej liście, ale jeśli możesz to zrobić od samego początku, jest to o wiele szybsze. Może być konieczne przekonwertowanie list2 (większej listy) na defaultdict, gdzie kluczem jest potencjalna wartość, którą chcesz sprawdzić z małej listy, a wartość to 1 (trafienie) lub 0 (brak trafienia, domyślnie).

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
źródło
-4

Prosty.

_new_list = []
for item in a:
    if item in b:
        _new_list.append(item)
    else:
        pass
PyGuy
źródło
1
To nie odpowiada na pytanie. OP chce wiedzieć, czy jakaś wartość z listy a znajduje się na liście b.
That1Guy,