Search code examples
pythonlistfor-loopwhile-loop

How to check if all elements of a list match a condition?


I have a list that contains many sub-lists of 3 elements each, like:

my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]

The last element of each sub-list is a sort of flag, which is initially 0 for each sub-list. As my algorithm progresses, I want to check whether this flag is 0 for at least one element. Currently I use a while loop, like so:

def check(list_):
    for item in list_:
        if item[2] == 0:
            return True
    return False

The overall algorithm loops as long as that condition is satisfied, and sets some of the flags in each iteration:

while check(my_list):
    for item in my_list:
        if condition:
            item[2] = 1
        else:
            do_sth()

Because it causes problems to remove elements from the list while iterating over it, I use these flags to keep track of elements that have already been processed.

How can I simplify or speed up the code?


See also Pythonic way of checking if a condition holds for any element of a list for checking the condition for any element. Keep in mind that "any" and "all" checks are related through De Morgan's law, just as "or" and "and" are related.

Existing answers here use the built-in function all to do the iteration. See How do Python's any and all functions work? for an explanation of all and its counterpart, any.

If the condition you want to check is "is found in another container", see How to check if all of the following items are in a list? and its counterpart, How to check if one of the following items is in a list?. Using any and all will work, but more efficient solutions are possible.


Solution

  • The best answer here is to use all(), which is the builtin for this situation. We combine this with a generator expression to produce the result you want cleanly and efficiently. For example:

    >>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
    >>> all(flag == 0 for (_, _, flag) in items)
    True
    >>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
    >>> all(flag == 0 for (_, _, flag) in items)
    False
    

    Note that all(flag == 0 for (_, _, flag) in items) is directly equivalent to all(item[2] == 0 for item in items), it's just a little nicer to read in this case.

    And, for the filter example, a list comprehension (of course, you could use a generator expression where appropriate):

    >>> [x for x in items if x[2] == 0]
    [[1, 2, 0], [1, 2, 0]]
    

    If you want to check at least one element is 0, the better option is to use any() which is more readable:

    >>> any(flag == 0 for (_, _, flag) in items)
    True