176

I found, that there is related question, about how to find if at least one item exists in a list:
How to check if one of the following items is in a list?

But what is the best and pythonic way to find whether all items exists in a list?

Searching through the docs I found this solution:

>>> l = ['a', 'b', 'c']
>>> set(['a', 'b']) <= set(l)
True
>>> set(['a', 'x']) <= set(l)
False

Other solution would be this:

>>> l = ['a', 'b', 'c']
>>> all(x in l for x in ['a', 'b'])
True
>>> all(x in l for x in ['a', 'x'])
False

But here you must do more typing.

Is there any other solutions?

Community
  • 1
  • 1
sirex
  • 4,593
  • 2
  • 32
  • 21
  • 6
    What's wrong with `set(smaller) <= set(larger)` ? – eumiro Oct 14 '10 at 08:58
  • 1
    I think your second solutions with 'all' looks just fine and pythonic to me. – Jiho Noh Jun 24 '18 at 14:25
  • Same question as [Python: See if one set contains another entirely? - Stack Overflow](https://stackoverflow.com/questions/2765892/python-see-if-one-set-contains-another-entirely) except for the list/set distinction. – user202729 Dec 05 '21 at 18:00

8 Answers8

278

Operators like <= in Python are generally not overriden to mean something significantly different than "less than or equal to". It's unusual for the standard library does this--it smells like legacy API to me.

Use the equivalent and more clearly-named method, set.issubset. Note that you don't need to convert the argument to a set; it'll do that for you if needed.

set(['a', 'b']).issubset(['a', 'b', 'c'])
Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • 5
    didn't know you could pass the list directly as an argument to issubset ... nice ! – tsimbalar Oct 14 '10 at 09:10
  • 4
    While I agree with the sentiment, I'm pretty OK with the idea of `<=` and `issubset` meaning the same thing. Why do you dislike it? – Kirk Strauser Oct 14 '10 at 13:25
  • 8
    @Just: Primarily, because it's not obvious what `<=` means for a set without either looking it up in the docs or having a prior knowledge of what it means in set theory, whereas everyone knows what `issubset` means automatically. – Glenn Maynard Oct 14 '10 at 21:04
  • 6
    You know the mathematical operator for (non-proper) subset? it basically looks pretty much like a rounded <= ;) – dom0 Aug 14 '13 at 22:48
  • love this solution. is there a way to get an index location or a list value instead of a bool (True:False)? – Vlad Gulin Nov 16 '18 at 23:05
  • The (non-proper) subset mathematical operator is a (partial) order relation (in a context where it makes sense). Thus it makes sense to denote it `<=` – tbrugere Aug 15 '22 at 10:24
72

I would probably use set in the following manner :

set(l).issuperset(set(['a','b'])) 

or the other way round :

set(['a','b']).issubset(set(l)) 

I find it a bit more readable, but it may be over-kill. Sets are particularly useful to compute union/intersection/differences between collections, but it may not be the best option in this situation ...

tsimbalar
  • 5,790
  • 6
  • 37
  • 61
  • Actually, `MySet.issubset(MyOtherSet)` and `MySet <= MyOtherSet` are the same. – Wok Oct 14 '10 at 09:03
  • 1
    @wok : oh I didn't know that, but I think the <= syntax is a bit confusing as a similar syntax can be used with lists, but with a very different meaning. – tsimbalar Oct 14 '10 at 09:07
  • 3
    it's not really that confusing if you recall the inclusion defines a partial order on any set of sets. It's actually slightly confusing that `<=` has the meaning it does for sequences: one might expect it to mean 'is a subsequence` of rather than lexicographical ordering. – aaronasterling Oct 14 '10 at 09:12
  • 1
    @aaronasterling : mmm, I personnally don't think too much about "partial order" when I type code :-), but I agree on the fact that using `<=` with sequences also feels strange, somehow ... – tsimbalar Oct 14 '10 at 09:16
  • Comparison operators only defining a partial order--whether or not you know what it's called :)--are fairly unusual. I think most people intuitively expect that `!(a <= b)` implies `b <= a`, which a partial order does not--both `set([1]) <= set([2])` and `set([2]) <= set([1])` are false. – Glenn Maynard Oct 14 '10 at 09:35
  • 6
    I ran into a little gotcha here I'd like to mention: If you use this method, you *are* converting your lists to sets, which means no duplicates. `set(['a','a']).issubset(['a'])` returns `True`. – Orangestar Dec 22 '15 at 08:47
22

I like these two because they seem the most logical, the latter being shorter and probably fastest (shown here using set literal syntax which has been backported to Python 2.7):

all(x in {'a', 'b', 'c'} for x in ['a', 'b'])
#   or
{'a', 'b'}.issubset({'a', 'b', 'c'})
martineau
  • 119,623
  • 25
  • 170
  • 301
  • The "all" solution is the quickest when you measure it with timeit(). This should be the accepted answer. – Attersson Feb 06 '19 at 12:05
12

What if your lists contain duplicates like this:

v1 = ['s', 'h', 'e', 'e', 'p']
v2 = ['s', 's', 'h']

Sets do not contain duplicates. So, the following line returns True.

set(v2).issubset(v1)

To count for duplicates, you can use the code:

v1 = sorted(v1)
v2 = sorted(v2)


def is_subseq(v2, v1):
    """Check whether v2 is a subsequence of v1."""
    it = iter(v1)
    return all(c in it for c in v2) 

So, the following line returns False.

is_subseq(v2, v1)
Max
  • 1,685
  • 16
  • 21
2

Not OP's case, but - for anyone who wants to assert intersection in dicts and ended up here due to poor googling (e.g. me) - you need to work with dict.items:

>>> a = {'key': 'value'}
>>> b = {'key': 'value', 'extra_key': 'extra_value'}
>>> all(item in a.items() for item in b.items())
True
>>> all(item in b.items() for item in a.items())
False

That's because dict.items returns tuples of key/value pairs, and much like any object in Python, they're interchangeably comparable

Julio Cezar Silva
  • 2,148
  • 1
  • 21
  • 30
2

Another solution would be:

l = ['a', 'b', 'c']
potential_subset1 = ['a', 'b']
potential_subset2 = ['a', 'x']
print(False not in [i in l for i in potential_subset1]) # True
print(False not in [i in l for i in potential_subset2]) # False

What makes my solution great is that you can write one-liners by putting the lists inline.

Johann Lau
  • 173
  • 1
  • 15
1

An example of how to do this using a lambda expression would be:

issublist = lambda x, y: 0 in [_ in x for _ in y]
heckj
  • 7,136
  • 3
  • 39
  • 50
Jundullah
  • 113
  • 2
  • 11
  • 3
    Please add comments to explain/elaborate your answer – Sharad Jan 29 '19 at 17:41
  • 2
    Note that using `_` as a variable name is confusing in this case. This is because by convention, `_` is used for a variable whose value you do not use ("throwaway variable"). see: https://stackoverflow.com/a/5893946/4948719 – tbrugere Aug 15 '22 at 10:27
  • @Nephanth 3 years later... thanks now I know. – Jundullah Aug 15 '22 at 15:38
0

Short syntax

I discovered a very readable syntax while experimenting on the Python interpreter.

>>> my_list = [1, 2, 3, 4, 5]
>>> (6 or 7) in my_list
False
>>> (2 or 6) in my_list
True
>>> (2 and 6) in my_list
False
>>> (2 and 5) in my_list
True

List of items to search for

If you have a long list of objects to search for, held in a sub_list variable:

>>> my_list = [1, 2, 3, 4, 5]
>>> sub_list = ['x', 'y']

If any (at least one) item is contained in the superset (or statement):

>>> next((True for item in sub_list if next((True for x in my_list if x == item), False)), False)
False

>>> sub_list[0] = 3
>>> next((True for item in sub_list if next((True for x in my_list if x == item), False)), False)
True

If all items are contained in superset (and statement), then sub_list is a full subset. Also featuring a bit of De Morgan's Law:

>>> next((False for item in sub_list if item not in my_list), True)
False

>>> sub_list[1] = 2
>>> next((False for item in sub_list if item not in my_list), True)
True
>>> next((True for item in sub_list if next((True for x in my_list if x == item), False)), False)
True
Michael
  • 8,362
  • 6
  • 61
  • 88
Emirhan Özlen
  • 474
  • 2
  • 14
  • 1
    The top syntax _looks nice,_ but it's misleading. It's actually only using one number in the `in` statement. So `(2 or 6) in my_list` is `True` because `(2 or 6)` is `2`, but `(6 or 2) in my_list` is `False` because `(6 or 2)` is `6`. – Michael Feb 10 '23 at 15:55