Using any() and all() to check if a list contains one set of values or another

Posted on

Question :

Using any() and all() to check if a list contains one set of values or another

My code is for a Tic Tac Toe game and checking for a draw state but I think this question could be more useful in a general sense.

I have a list that represents the board, it looks like this:

``````board = [1,2,3,4,5,6,7,8,9]
``````

When a player makes a move the int they moved on is replaced with their marker (‘x’ or ‘o’), I already have checks in place to look for a winning state, what I can’t do is check for a draw state, where none of the list values are ints but a winning state has not been set.

The code I have so far:

``````if any(board) != playerOne or any(board) != playerTwo:
print 'continue'
elif all(board) == playerOne or playerTwo:
print 'Draw'
``````

The if statement works, the elif does not, I think the problem is my ‘or’ operator, what I want to check for is: if the every item on the board is either playerOne marker or playerTwo marker, if I where to make the code:

``````elif all(board) == playerOne or all(board) == playerTwo:
``````

I would be checking to see if every place on the board was playerOne or every place on the board is playerTwo, which it won’t be.

So how do I check if the board is taken up by a combination of playerOne markers and playerTwo markers?

Generally speaking:

`all` and `any` are functions that take some iterable and return `True`, if

• in the case of `all()`, no values in the iterable are falsy;
• in the case of `any()`, at least one value is truthy.

A value `x` is falsy iff `bool(x) == False`.
A value `x` is truthy iff `bool(x) == True`.

Any non-booleans in the iterable will be fine — `bool(x)` will coerce any `x` according to these rules: `0`, `0.0`, `None`, `[]`, `()`, `[]`, `set()`, and other empty collections will yield `False`, anything else `True`. The docstring for `bool` uses the terms ‘true’/’false’ for ‘truthy’/’falsy’, and `True`/`False` for the concrete boolean values.

You misunderstood a little bit how these functions work. Hence, the following does something completely not what you thought:

``````if any(foobars) == big_foobar:
``````

…because `any(foobars)` would first be evaluated to either `True` or `False`, and then that boolean value would be compared to `big_foobar`, which generally always gives you `False` (unless `big_foobar` coincidentally happened to be the same boolean value).

Note: the iterable can be a list, but it can also be a generator/generator expression (? lazily evaluated/generated list) or any other iterator.

``````if any(x == big_foobar for x in foobars):
``````

which basically first constructs an iterable that yields a sequence of booleans—for each item in `foobars`, it compares the item to `big_foobar` and emits the resulting boolean into the resulting sequence:

``````tmp = (x == big_foobar for x in foobars)
``````

then `any` walks over all items in `tmp` and returns `True` as soon as it finds the first truthy element. It’s as if you did the following:

``````In [1]: foobars = ['big', 'small', 'medium', 'nice', 'ugly']

In [2]: big_foobar = 'big'

In [3]: any(['big' == big_foobar, 'small' == big_foobar, 'medium' == big_foobar, 'nice' == big_foobar, 'ugly' == big_foobar])
Out[3]: True
``````

Note: As DSM pointed out, `any(x == y for x in xs)` is equivalent to `y in xs` but the latter is more readable, quicker to write and runs faster.

Some examples:

``````In [1]: any(x > 5 for x in range(4))
Out[1]: False

In [2]: all(isinstance(x, int) for x in range(10))
Out[2]: True

In [3]: any(x == 'Erik' for x in ['Erik', 'John', 'Jane', 'Jim'])
Out[3]: True

In [4]: all([True, True, True, False, True])
Out[4]: False
``````