Efficient way to count True and False

This may be a trivial problem, but I want to learn more about other smarter and more effective ways to solve it.

I have a list of elements, and each element has property a , whose value is binary.

  • If each item in the list has a == 0 , then I set a separate variable b = 0 .
  • If every item in the list has a == 1 , then I set b = 1 .
  • If there is a mixture of a == 0 and a == 1 in the list, then I set b = 2 .

I can use the set to track the types of values ​​of a , so if there are two elements in the list after repeating the list, then I can set b = 2 , whereas if there is only one element in the set, I simply extract the element (0 or 1 ) and use it to install b .

Any better way?

+6
source share
10 answers

I would suggest using any and all . I would say that the advantage of this is readability rather than skill or effectiveness. For instance:

 >>> vals0 = [0, 0, 0, 0, 0] >>> vals1 = [1, 1, 1, 1, 1] >>> vals2 = [0, 1, 0, 1, 0] >>> def category(vals): ... if all(vals): ... return 1 ... elif any(vals): ... return 2 ... else: ... return 0 ... >>> category(vals0) 0 >>> category(vals1) 1 >>> category(vals2) 2 

This can be reduced a little if you want:

 >>> def category(vals): ... return 1 if all(vals) else 2 if any(vals) else 0 ... 

This works with everything that __nonzero__ (or __bool__ in Python 3) can interpret as having a true or false value.

+18
source

One pass through the list and no additional data structures are built:

 def zot(bs): n, s = len(bs), sum(bs) return 1 if n == s else 2 if s else 0 
+26
source

Someone mentioned the golf code, so they cannot resist the variation on @ senderle's:

 [0,2,1][all(vals) + any(vals)] 

Brief Explanation: Boolean values ​​are used here as their integer equivalents to index the list of desired answers. If all true, then any must also be true, so their sum is 2 . any alone gives 1 , and no matches give 0 . These indexes return the corresponding values ​​from the list.

If the original requirements can be changed to use 1 for any and 2 for all , it would be even easier to just return the integer any + all

+15
source

Using a dictionary:

 zonk_values = {frozenset([0]): 0, frozenset([1]): 1, frozenset([0, 1]): 2} def zonk(a): return zonk_values[frozenset(a)] 

It also requires only one pass through the list.

+3
source

You can also use kits.

 s = set([ia for i in your_list]) if len(s) == 1: b = s.pop() else: b = 2 
+2
source
 def zot(bs): return len(set(bs)) if sum(bs) else 0 
+2
source

You can define two boolean vars hasZero and hasOne and set them to True if the corresponding value was executed during iteration of the list. Then b = 2 if hasZero and hasOne , b = 1 , if only hasOne and b = 0 , if only hasZero .

Another way: you can sum all the values ​​of a over a list. If sumA == len(list) , then b = 1 , if sumA == 0 , then b = 0 , and if 0 < sumA < len(list) , then b = 2 .

+1
source

Short circuit. Probably the most efficient way to do this in Python.

EDIT: any and all included as suggested in the comments.

EDIT2: Now it is single-line.

 b = 1 if all(A) else 2 if any(A) else 0 
+1
source

This is similar to the senderle suggestion, but to access the properties of a objects.

 from random import randint class Item(object): def __init__(self, a): self.a = a all_zeros = [Item(0) for _ in xrange(10)] all_ones = [Item(1) for _ in xrange(10)] mixture = [Item(randint(0, 1)) for _ in xrange(10)] def check(items): if all(item.a for item in items): return 1 if any(item.a for item in items): return 2 else: return 0 print 'check(all_zeros):', check(all_zeros) print 'check(all_ones):', check(all_ones) print 'check(mixture):', check(mixture) 
0
source

You can use a list of iter ators:

 >>> L = [0, 0, 0, 0, 0] >>> L1 = [1, 1, 1, 1, 1] >>> L2 = [0, 1, 0, 1, 0] >>> def fn(i): ... i = iter(i) ... if all(i): return 1 ... return 2 if any(i) else 0 ... >>> fn(L) 0 >>> fn(L1) 1 >>> fn(L2) 2 
0
source

Source: https://habr.com/ru/post/955420/


All Articles