0

I have python list like below

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]]*4

I want to count '3' , how many time its in this list, I have tried with for loop but it not count in side the another list and tuple, How to do it ?

expected output is 20.

R.J. Dunnill
  • 2,049
  • 3
  • 10
  • 21
edward
  • 29
  • 1
  • 7

7 Answers7

3

You can first flatten your irregular list using this method and then apply count(3)

from collections import Iterable, Counter

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]]*4

def flatten(l):
    for el in l:
        if isinstance(el, Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten(el)
        else:
            yield el

freqs = list(flatten(x)).count(3)            

# 20

For reasons pointed out again by @bhlsing below, you can just iterate through the list ad count the occurrences of 3 and sum

sum(1 for i in flatten(x) if i == 3)

Alternatively You can also use Counter if you want the frequency of all elements. For a single element, this would be an overkill as pointed out by @bhlsing

freqs = Counter(flatten(x))
print (freqs[3])
Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • Note that converting the returning iterable to a list unnecessarily makes the solution *O(n)* in space complexity. Use `sum(1 for i in flatten(x) if i == 3)` instead to make the solution cost *O(1)* in space. – blhsing Jun 13 '19 at 18:20
  • 1
    @blhsing : Thanks again. I just did that because `count` doesn't work with generator objects. Let me edit again :) – Sheldore Jun 13 '19 at 18:21
  • @blhsing : But iterating through the whole list using `for i in flatten(x) if i==3` will also go through all the `n` elements. So how is it not *O(n)*? – Sheldore Jun 13 '19 at 18:23
  • I'm talking about the cost in space, not time. The solution costs *O(n)* in time already due to the `flatten` function visiting all items. – blhsing Jun 13 '19 at 18:24
  • The time complexities of the two are the same, yes, but I wouldn't say the performance is identical because of the different overheads involved in the two different implementations. – blhsing Jun 13 '19 at 18:26
0

Slow, hacky implementation of recursive count:

def recursive_count(lst, item):
    count = 0
        for elem in lst:
            if elem == item:
                count += 1
            elif type(elem) in (list, dict, set, tuple):  # or something else to check for iterale types
                count += recursive_count(elem, item)
    return count
Green Cloak Guy
  • 23,793
  • 4
  • 33
  • 53
0

You can check if an object is iterable by using the hasattr(obj, '__iter__') call

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]]*4

threes = 0

for item in x:
    # object is iterable, iterate over it
    if hasattr(item '__iter__'):
        for sub_item in item:
            if sub_item==3:
                threes +=1
    # otherwise it isn't, go ahead and do an equality check
    elif item==3:
        threes +=1

threes
20

To make this a recursive solution similar to others that are posted, be wary of hasattr(obj, '__iter__'), as for str types this will lead to infinite recursion. To avoid this:

def count_threes(x):
    count = 0
    for item in x:
        if isinstance(item, str) or isinstance(item, bytes):
            continue # skip to avoid infinite recursion
        if hasattr(item, '__iter__'): # is an iterable
            count += count_threes(item)
        elif item == 3:
            count += 1
    return count

count_threes(x)
20
C.Nivs
  • 12,353
  • 2
  • 19
  • 44
  • Please note this has a limit of only one level of nested lists. – double_j Jun 13 '19 at 18:15
  • @double_j true, but there are already multiple recursion options, so I'm not sure this would be sufficiently different to justify modifying it – C.Nivs Jun 13 '19 at 18:20
  • @edy please note that if you apply this in a recursive scenario, it will get trapped on any `str`, since `str` is iterable, no matter how small the unit is. I'll make an edit with that in mind, especially since somehow this wound up being accepted – C.Nivs Jun 13 '19 at 18:28
0

Function:

def count_element(obj, el):
    occurences = 0
    for item in obj:
        if isinstance(item, (list, tuple, set)):
            occurences += count_element(item, el)
        elif isinstance(item, type(el)):
            occurences += int(item == el)
    return occurences

Usage:

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]] * 4
count = count_element(x, 3)

Output:

20
Olvin Roght
  • 7,677
  • 2
  • 16
  • 35
0

One of many ways:

def flatten(*args):
    for x in args:
        if isinstance(x, (list, tuple)):
            for y in flatten(*x):
                yield y
        else:
            yield x

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]]*4

print(len(list(filter(lambda x: x == 3, flatten(x)))))  
Booboo
  • 38,656
  • 3
  • 37
  • 60
0

In this solution, you just check every list and search for 3 in it

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]]*4
counter = 0
stash = []
stash.append(x)
while len(stash)!=0:
    print(stash)
    list = stash[0]
    for element in list:
        if hasattr(element, '__iter__') and not isinstance(element, str):
            stash.append(element)
        if element == 3:
            counter += 1
    stash.remove(list)
print(counter)

`

0

You should probably check for types list and tuple specifically. Otherwise you won't properly count strings in the multi-level list.

here's a small recursive function that will do it:

x = [False, 44, 3, 56, 3, [33, 45, 66, 3], ('c', 3), [4, 3]]*4

def deepcount(value,target):
    if not isinstance(value,(list,tuple)):
        return int(value==target)
    return sum(deepcount(v,target) for v in value)

print(deepcount(x,3)) # 20

it will properly count strings in the structure:

y = ["abc", 12, "a",[23, False,"abc"]]*3

print(deepcount(y,"abc")) # 6
print(deepcount(y,"a"))   # 3
Alain T.
  • 40,517
  • 4
  • 31
  • 51