How can I check that a list has one and only one truthy value?

后端 未结 17 2165
暗喜
暗喜 2020-11-27 12:23

In python, I have a list that should have one and only one truthy value (that is, bool(value) is True). Is there a clever way to check for this

相关标签:
17条回答
  • 2020-11-27 13:15

    @JonClements` solution extended for at most N True values:

    # Extend any() to n true values
    def _NTrue(i, n=1):
        for x in xrange(n):
            if any(i): # False for empty
                continue
            else:
                return False
        return True
    
    def NTrue(iterable, n=1):
        i = iter(iterable)
        return any(i) and not _NTrue(i, n)
    

    edit: better version

    def test(iterable, n=1): 
        i = iter(iterable) 
        return sum(any(i) for x in xrange(n+1)) <= n 
    

    edit2: include at least m True's and at most n True's

    def test(iterable, n=1, m=1): 
        i = iter(iterable) 
        return  m <= sum(any(i) for x in xrange(n+1)) <= n
    
    0 讨论(0)
  • 2020-11-27 13:16

    It depends if you are just looking for the value True or are also looking for other values that would evaluate to True logically (like 11 or "hello"). If the former:

    def only1(l):
        return l.count(True) == 1
    

    If the latter:

    def only1(l):
        return sum(bool(e) for e in l) == 1
    

    since this would do both the counting and the conversion in a single iteration without having to build a new list.

    0 讨论(0)
  • 2020-11-27 13:17

    You can do:

    x = [bool(i) for i in x]
    return x.count(True) == 1
    

    Or

    x = map(bool, x)
    return x.count(True) == 1
    

    Building on @JoranBeasley's method:

    sum(map(bool, x)) == 1
    
    0 讨论(0)
  • 2020-11-27 13:19

    For completeness' sake and to demonstrate advanced use of Python's control flow for for loop iteration, one can avoid the extra accounting in the accepted answer, making this slightly faster.:

    def one_bool_true(iterable):
        it = iter(iterable)
        for i in it:
            if i:
                break
        else:            #no break, didn't find a true element
            return False
        for i in it:     # continue consuming iterator where left off
            if i: 
                return False
        return True      # didn't find a second true.
    

    The above's simple control flow makes use of Python's sophisticated feature of loops: the else. The semantics are that if you finish iterating over the iterator that you are consuming without break-ing out of it, you then enter the else block.

    Here's the accepted answer, which uses a bit more accounting.

    def only1(l):
        true_found = False
        for v in l:
            if v:
                # a True was found!
                if true_found:
                    # found too many True's
                    return False 
                else:
                    # found the first True
                    true_found = True
        # found zero or one True value
        return true_found
    

    to time these:

    import timeit
    >>> min(timeit.repeat(lambda: one_bool_true([0]*100 + [1, 1])))
    13.992251592921093
    >>> min(timeit.repeat(lambda: one_bool_true([1, 1] + [0]*100)))
    2.208037032979064
    >>> min(timeit.repeat(lambda: only1([0]*100 + [1, 1])))
    14.213872335107908
    >>> min(timeit.repeat(lambda: only1([1, 1] + [0]*100)))
    2.2482982632641324
    >>> 2.2482/2.2080
    1.0182065217391305
    >>> 14.2138/13.9922
    1.0158373951201385
    

    So we see that the accepted answer takes a bit longer (slightly more than one and a half of a percent).

    Naturally, using the built-in any, written in C, is much faster (see Jon Clement's answer for the implementation - this is the short form):

    >>> min(timeit.repeat(lambda: single_true([0]*100 + [1, 1])))
    2.7257133318785236
    >>> min(timeit.repeat(lambda: single_true([1, 1] + [0]*100)))
    2.012824866380015
    
    0 讨论(0)
  • 2020-11-27 13:21

    If there is only one True, then the length of the Trues should be one:

    def only_1(l): return 1 == len(filter(None, l))
    
    0 讨论(0)
提交回复
热议问题