7

I'm trying to write a function count(s, chars) that takes a string s and a list of characters chars. The function should count the number of occurrences of the letters given in chars. It should return a dictionary where the keys are the characters given in the list of characters chars.

So for example:

In [1]: s = "Another test string with x and y but no capital h."
In [2]: count(s, ['A', 'a', 'z'])
Out[2]: 'A': 1, 'a': 3, 'z': 0

I made some code that can count all the characters of the string and return a dictionary of it:

return {i: s.count(i) for i in set(s)}

but I'm not sure how you would use a list of specific characters and return a dictionary...

Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
Nick Charlton
  • 109
  • 1
  • 1
  • 6

6 Answers6

6

What about:

def count_chars(s,chars):
    return {c : s.count(c) for c in chars}

Generates:

$ python3
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> s = "Another test string with x and y but no capital h."
>>> def count_chars(s,chars):
...     return {c : s.count(c) for c in chars}
... 
>>> count_chars(s, ['A', 'a', 'z'])
{'z': 0, 'A': 1, 'a': 3}

Although this is rather inefficient. Probably a more efficiency way is do the counting in one step. You can use a Counter for this and then retain the interesting characters:

from collections import Counter

def count_chars(s,chars):
    counter = Counter(s)
    return {c : counter.get(c,0) for c in chars}
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • well `Counter` is not as fast as `str.count` (see also [Why is Collections.counter so slow?](http://stackoverflow.com/a/41595097/5393381)). You need to look for _a lot of different characters_ with `Counter` to only come close to the `str.count` performance. – MSeifert Jan 15 '17 at 19:06
  • @MSeifert: but the point is that you will only need to loop once through the string. If you are interested in say 100 characters, you would have to use `str.count` 100 times, which will probably be slower than looping through it once using a `Counter`. – Willem Van Onsem Jan 15 '17 at 19:14
  • Well if you read the answer carefully, you will see I already claim this is not a good way to solve the problem and propose to use a counter. The first answer was more to show the OP a way to alter their implementation. Nevertheless the funny part is that MSeifert claims the opposite, so one of you is wrong :). I think it depends both on the length of the string and the number of characters one aims to count. – Willem Van Onsem Jan 16 '17 at 08:49
3
str.count(sub[, start[, end]])

Return the number of non-overlapping occurrences of substring sub in the range [start, end]. Optional arguments start and end are interpreted as in slice notation.

e.g. usage

>>> sentence = 'Mary had a little lamb'
>>> sentence.count('a')
4

so count function can be easily used here in this scenario as well. Below is the sample code snippet of the function

li=['A', 'a', 'z']
s = "Another test string with x and y but no capital h."

def count(s, li):
    cnt=dict.fromkeys(li, 0)
    for c in li:
      cnt[c] = s.count(c)
    return cnt

Console output will be like

>>> count(s, li)
{'a': 3, 'A': 1, 'z': 0}
codythecoder
  • 376
  • 5
  • 17
Keshan Nageswaran
  • 8,060
  • 3
  • 28
  • 45
2

You can do it 'old style' by using the dict fromkeys method to set all keys zeros and then increment for each character:

li=['A', 'a', 'z']
s = "Another test string with x and y but no capital h."

def count(s, li):
    cnt={}.fromkeys(li, 0)
    for c in s:
        if c in cnt:
            cnt[c]=cnt[c]+1
    return cnt

>>> count(s, li)
{'A': 1, 'a': 3, 'z': 0}

Or, prefilter so you only test for keys that you are interested in:

def count(s, li):
    cnt={}.fromkeys(li, 0)
    for c in (e for e in s if e in cnt):
        cnt[c]+=1
    return cnt

But the fastest, most Pythonic is to use a Counter:

>>> from collections import Counter
>>> c=Counter(s)
>>> c
Counter({' ': 10, 't': 7, 'n': 4, 'h': 3, 'i': 3, 'a': 3, 'o': 2, 'e': 2, 'r': 2, 's': 2, 'A': 1, 'g': 1, 'w': 1, 'x': 1, 'd': 1, 'y': 1, 'b': 1, 'u': 1, 'c': 1, 'p': 1, 'l': 1, '.': 1})

Then construct your desired dict from that:

>>> {k:c[k] for k in li}
{'A': 1, 'a': 3, 'z': 0}
dawg
  • 98,345
  • 23
  • 131
  • 206
1
 def count(s, chars):
    ret = dict(zip(chars, [0 for c in chars]))
    for c in s:
        if ret.has_key(c):
            ret[c] += 1
    return ret

Something like that maybe.

quanke0801
  • 41
  • 5
1

You can also build a dictionary with zip built-in method:

>>> s
'Another test string with x and y but no capital h.'
>>> c
['A', 'a', 'z']
>>> def count_char(s, c):
       counts = map(s.count, c)
       return dict(zip(c, counts))

>>> 
>>> count_char(s, c)
{'z': 0, 'A': 1, 'a': 3}
Iron Fist
  • 10,739
  • 2
  • 18
  • 34
1

Well, so many answers, I will throw in mine too, which is based on built in constructs:

from collections import Counter

s = "Another test string with x and y but no capital h."
chars = ['A', 'a', 'z']

count = Counter(s)  # creates a dictionary
count = {k:v for k, v in count.items() if k in chars}  # take only charatcters from chars
count.update({k:0 for k in set(chars) - set(s)})  # add zero instances
print(count)

===
{'a': 3, 'A': 1, 'z': 0}
Iron Fist
  • 10,739
  • 2
  • 18
  • 34
Israel Unterman
  • 13,158
  • 4
  • 28
  • 35