1

I am attempting to count the number of occurrences of an ENUM in a string value e.g.

class numbers(Enum): 
   one = 1
   two = 2

string = "121212123324"

string.count(str(numbers.one.value))

This just seems very unintuitive to convert the enum back to string - are there any quicker ways?

Talha Tayyab
  • 8,111
  • 25
  • 27
  • 44
user5067291
  • 440
  • 1
  • 6
  • 16
  • 1
    you could maybe use [`collections.Counter`](https://docs.python.org/3/library/collections.html#collections.Counter)? – Matiiss Oct 31 '21 at 12:01
  • 1
    @Matiiss What's the advantage? That would still have the same "issue" they're asking about, it would be more code, and it would likely be slower. – Kelly Bundy Oct 31 '21 at 12:07
  • @Matiiss Hmm, I'm not convinced that extracting and converting many strings to ints is somehow better than converting one int to a string. – Kelly Bundy Oct 31 '21 at 12:11
  • 2
    @user If you want them to act as strings, maybe define them as such? Like `one = '1'`? – Kelly Bundy Oct 31 '21 at 12:14
  • @KellyBundy I just tested and indeed conversion to integers for use in counter is the slowest way to go about this (way worse than I initially thought), and using `.count` (with converting enum value to string) is the fastest. But if they need to count all enums, then using string with `Counter` (and the key being a `str()`) seems to be the best solution (also according to this answer: https://stackoverflow.com/a/2600208/14531062 (which use lists so I don't know if it directly translates to string but...)) – Matiiss Oct 31 '21 at 12:26
  • @KellyBundy edited answer, I think OP solution is good – I'mahdi Oct 31 '21 at 12:47
  • @Matiiss I'd say enums typically only have a handful of values, so it's unlikely that the Counter method would win. Counting a character in a string is just too fast (not comparable to counting a value in a list). In this case the enum is probably even limited to at most nine or ten values, as multidigit strings would introduce the issue of overlapping occurrences. – Kelly Bundy Oct 31 '21 at 13:25

1 Answers1

3

Your solution is good, you can see runtime of 5 approach in below:

from timeit import timeit
from collections import Counter
from enum import Enum

class numbers(Enum): 
    one = 1
    two = 2
    three = 3
    four = 4

def approach1(products):
    return Counter(products)[str(numbers.one.value)]

def approach2(products):
    return products.count(str(numbers.one.value))

def approach3(products):
    lst = list(map(int, products))
    return lst.count(int(numbers.one.value))

def approach4(products):
    cnt = Counter(products)
    return (cnt[str(numbers.one.value)] , str(numbers.two.value) , 
        cnt[str(numbers.three.value)] , str(numbers.four.value))

def approach5(products):
    cnt_o = products.count(str(numbers.one.value))
    cnt_t = products.count(str(numbers.two.value))    
    cnt_h = products.count(str(numbers.three.value))
    cnt_f = products.count(str(numbers.four.value))
    return (cnt_o , cnt_t , cnt_h , cnt_f)


funcs = approach1, approach2, approach3 , approach4, approach5
products = "121212123324"*10000000


for _ in range(3):
    for func in funcs:
        t = timeit(lambda: func(products), number=1)
        print('%.3f s ' % t, func.__name__)
    print()

Output:

6.279 s  approach1
0.140 s  approach2
17.172 s  approach3
6.403 s  approach4
0.491 s  approach5

6.340 s  approach1
0.139 s  approach2
16.049 s  approach3
6.559 s  approach4
0.474 s  approach5

6.245 s  approach1
0.143 s  approach2
15.876 s  approach3
6.172 s  approach4
0.475 s  approach5
I'mahdi
  • 23,382
  • 5
  • 22
  • 30