3

Say I have an list of values that contains some 0s and some Falses. Something like

arr = [0, False, False, 0, False]

How would I count each of them in Python?

list.count doesn't work here:

>>> arr.count(0)
5
>>> arr.count(False)
5
MSeifert
  • 145,886
  • 38
  • 333
  • 352
Ariel Voskov
  • 326
  • 3
  • 7
  • What is the actual problem you are trying to solve? I suspect there might be a better way in the first place. For example, if you want to use placeholder elements in the list, it would be better to use `None` instead of `False`. – mkrieger1 Feb 01 '17 at 14:14
  • No particular problem. THIS is what I was trying to figure. – Ariel Voskov Feb 01 '17 at 20:53

4 Answers4

3

One option would be to use is to compare against False:

print(sum(1 for x in arr if x is False))

result: 3

(also works with is 0 BTW)

EDIT: we could also rely on the fact that True==1 after all: sum(x is False for x in arr) is even simpler.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • I think that there's no guarantee to have a single `0` object - this _will probably_ work, but it's not guaranteed. – miradulo Feb 01 '17 at 14:13
  • This is surely the best way because `0` and `False` are singletons right? – Chris_Rands Feb 01 '17 at 14:13
  • well, there is also no guarantee that `False` is a singleton :-) It just so happens to be the case in CPython (and probably every other python implementation because it "makes sense"). – MSeifert Feb 01 '17 at 14:14
  • This will be a lot slower than `list.count` though, would be nice if there was a `list.count` equivalent for identity (instead of equality)? – Chris_Rands Feb 01 '17 at 14:16
  • @Chris_Rands yes. But who puts booleans _and_ integers in his lists? interesting problem, though. – Jean-François Fabre Feb 01 '17 at 14:18
  • 1
    @MSeifert [Guido says they are singletons](http://stackoverflow.com/q/2172189/216074). That’s as close as “per spec” as you can probably get :P – poke Feb 01 '17 at 14:37
  • I wrote this answer with the classical pythonic `is None` in mind. `None` is also a singleton. – Jean-François Fabre Feb 01 '17 at 15:03
  • I've actually found a little simpler way to implement this: `sum(x is False for x in arr) ==> 3` and `sum(x is 0 for x in arr)` ==> 2` – Ariel Voskov Feb 01 '17 at 20:55
  • Yeah, that's also why I included it (already) in my answer ;-) – MSeifert Feb 01 '17 at 21:00
3

The only way to distinguish is by their type:

>>> type(False)
bool
>>> type(0)
int

But isinstance can't be used for 0 because bool is a subclass of int

>>> isinstance(0, bool)
False
>>> isinstance(False, int)
True

And the values are just equal (that's why count doesn't work "correctly" for you):

>>> 0 == False
True

You could use the fact that 0 is an interned (singleton) integer in CPython (probably also other Python implementations) and False is also a singleton:

>>> 0 is 0
True
>>> False is False
True
>>> 0 is False
False

As for a solution: Use something that isn't so equal, for example None:

arr = [0, None, None, 0, None]

arr.count(0)     # 2
arr.count(None)  # 3

If you really need to use 0 and False you could use sum (like others have shown):

sum(x is False for x in arr)  # for count(False)
sum(x is 0 for x in arr)      # for count(0)

Or Python-implementation independant: the type and value as identifier and collections.Counter:

>>> arr = [0, False, False, 0, False]

>>> from collections import Counter

>>> Counter((type(x), x) for x in arr)
Counter({(bool, False): 3, (int, 0): 2})
MSeifert
  • 145,886
  • 38
  • 333
  • 352
  • What exactly is the problem with using `isinstance` here? You could easily check if it’s *not* a bool? – poke Feb 01 '17 at 14:40
  • It's no problem, but the logic is different: `if isinstance(x, bool)` (to count `False`) and `if **not** isinstance(x, bool)` (to count `0`). That doesn't generalize "well". – MSeifert Feb 01 '17 at 14:42
  • `not isinstance(x, bool) and x == 0`? – poke Feb 01 '17 at 14:46
  • @poke I meant the `not` differs between both approaches, that makes it asymmetric. I wanted to show rather general approaches that work if instances are equal but their classes differ. The `sum(x is 0 for x in arr)` and `sum(x is False for x in arr)` are not general but at least symmetric. For example it get's really complicated when it contains floats `0.0`, ints `0` and bools `False`. :D – MSeifert Feb 01 '17 at 14:50
0

This is a rare case where it seems sensible to just use a type check.

sum(1 for item in arr if item == 0 and type(item) is type(0))

As mentioned by Jean-François, if you're guaranteed to only have 0 and False, the type check is sufficient on its own.

miradulo
  • 28,857
  • 6
  • 80
  • 93
0

Here is one way using defaultdict on order to find the count of both 0 and False at once:

In [73]: from collections import defaultdict

In [74]: d = defaultdict(int)

In [75]: for i in arr:
   ....:     d[str(i)] += i is False or 1 # if I is not False is 0
   ....:     

In [76]: d
Out[76]: defaultdict(<class 'int'>, {'0': 2, 'False': 3})
Mazdak
  • 105,000
  • 18
  • 159
  • 188