1

I have a list of objects with a boolean property.

I'd like to count these objects according to this boolean property - None's included.

Are there functions in the standard library that would be more efficient, terser, or more Pythonic than fully writing out a loop to iterate over them?

Counter does not seem appropriate, and this nor this solve the problem - as far as I can tell.

class MyObject():
    def __init__(self, moniker, booleanVariable):
        self.name = moniker
        self.state = booleanVariable

def _myCounterMethod(list):
    # code goes here
    return counts

myList = []
myList.append(MyObject("objectOne", True))
myList.append(MyObject("objectTwo", False))
myList.append(MyObject("objectThree", None))

counts = _MyCounterMethod(myList)
print(counts)
>>>[('True', 1), ('False', 1), ('None', 1)]

My current solution:

def _myCounterMethod(list):
    trueCount = 0
    falseCount = 0
    noneCount = 0
    for obj in list:
        if obj.state == True:
            trueCount += 1
        elif obj.state == False:
            falseCount += 1
        elif obj.state == None:
            noneCount += 1
    countsList = [('True', trueCount), ('False', falseCount), ('None', noneCount)]
    return countsList
farAndAway
  • 33
  • 6

4 Answers4

3

You are already covered with Counter based approaches, here is a simple shortening of what you have:

def _myCounterMethod(lst):  # do not shadow `list`
    trueCount = falseCount = noneCount = 0
    for obj in lst:
        trueCount += obj.state is True
        falseCount += obj.state is False
        noneCount += obj.state is None
    return [('True', trueCount), ('False', falseCount), ('None', noneCount)]

And for my take on the best approach:

from collections import Counter
from operator import attrgetter

def _myCounterMethod(lst):
    return Counter(map(attrgetter("state"), lst)).most_common()
    # already a list of tuples

Convert whatever you have to strings only when you are printing it really and really need to.

user2390182
  • 72,016
  • 6
  • 67
  • 89
  • 1
    May or may not be what is desired, but it's already a list and since it is destined for output, descending sort order can only be a plus :-) – user2390182 Oct 11 '21 at 16:21
1

Use collections.Counter:

import collections


class MyObject():
    def __init__(self, moniker, booleanVariable):
        self.name = moniker
        self.state = booleanVariable


my_list = [MyObject("objectOne", True), MyObject("objectTwo", False), MyObject("objectThree", None)]

counts = collections.Counter(str(e.state) for e in my_list)
print(counts)

Output

Counter({'True': 1, 'False': 1, 'None': 1})

If strictly list output is needed do:

result = list(counts.items())
print(result)

Output

[('True', 1), ('False', 1), ('None', 1)]
Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
  • @schwobaseggl It's not a hard requirement, but the end utility _is_ printing state to the console - so it would be convenient. – farAndAway Oct 11 '21 at 16:12
1

I suggest you implement the .count() method

def counter(l):
   iter = [x.state for x in l]
   return ('True', iter.count(True)), ('False', iter.count(False)), ('None', iter.count(None))
>>> counter(myList)
(('True', 1), ('False', 1), ('None', 1))
  • According to [this question](https://stackoverflow.com/questions/44812468/what-is-the-time-complexity-of-python-lists-count-function), count makes use of a loop that iterates over the entire list to find the value of the provided field. So while this is quite readable, you are iterating over the list's length 4 times in total – farAndAway Oct 11 '21 at 16:31
0

The below seems to work

from collections import defaultdict

data = defaultdict(int)
class MyObject():
    def __init__(self, moniker, booleanVariable):
        self.name = moniker
        self.state = booleanVariable
myList = []
myList.append(MyObject("objectOne", True))
myList.append(MyObject("objectTwo", False))
myList.append(MyObject("objectThree", None))
for o in myList:
  data[str(o.state)] += 1
print(data)

output

defaultdict(<class 'int'>, {'True': 1, 'False': 1, 'None': 1})
balderman
  • 22,927
  • 7
  • 34
  • 52