Steve8what
Steve8what

Reputation: 21

Counting list variables with dictionary

I have to make a program that can count the number of fruits in a given list.

I've tried a for loop but didn't have any success. I have also tried a Counter but that would remove the second 'banana' and only recognizes one.

c = { 'banana': 'fruit', 'apple': 'fruit', 'pizza': 'fastfood', 'fries': 'fastfood' }
v = [ 'fries', 'banana', 'banana', 'apple', 'pizza' ]

r = len([v for v in c if c.items() == 'fruit' in c.value()])

print(r)

I expected that this would give me the number of fruits but instead, I have an output of 0.

Upvotes: 1

Views: 122

Answers (5)

Patrick Artner
Patrick Artner

Reputation: 51643

You can construct a set from all fruits and use that:

c = { 'banana': 'fruit', 'apple': 'fruit', 'pizza': 'fastfood', 'fries': 'fastfood' }

# collect all fruits into a set that only contains fruit
fixed = set(k for k,v in c.items() if v=="fruit")  # cheating by creating the set in setup ;)

v = [ 'fries', 'banana', 'banana', 'apple', 'pizza' ]

r = sum(f in fixed for f in v)

print(r)

Returns 3 - because True ( the result of the O(1) set lookup) equals to 1 and False equals 0.


This solution is worse then Daniel Trugman's solution under most conditions, in other conditions it is slightly faster:

  • to create the set it accesses each value of the dict once to build the set - after that it is using the created set to decide if something is a fruit:

Consider:

c = {'banana':'fruit'}
v = ['banana' * 100000]
fixed = set(k for k,v in c.items() if v=="fruit")

The dict-comprehension directly has to acces the value of c['banana'] 100000 times and do 100000 c[val] == 'fruit' to check if it is a fruit. This is slower than checking 100000 times 'banana' in fixed on the constructed set.

For the example at hand the dict comp is fine.


Most of the time the dict-comp is faster:

setup_code = """
c = { 'banana': 'fruit', 'apple': 'fruit', 'pizza': 'fastfood', 'fries': 'fastfood' }
v = [ 'fries', 'banana', 'banana', 'apple', 'pizza' ]
fixed = set(k for k,v in c.items() if v=="fruit")
"""

setup_code2 = """
c = { 'banana': 'fruit', 'apple': 'fruit', 'pizza': 'fastfood', 'fries': 'fastfood' }
v = ['banana']*1000  
fixed = set(k for k,v in c.items() if v=="fruit")
"""

using_set = """
# collect all fruits into a set that only contains fruit

r = sum(1 if f in fixed else 0 for f in v)
"""

using_dict_comp = """
r = sum(1 for val in v if c[val] == 'fruit')
"""

import timeit

print("set:", timeit.timeit(using_set,setup=setup_code,number=10000))
print("dict:", timeit.timeit(using_dict_comp,setup=setup_code,number=1000))
print("set:", timeit.timeit(using_set,setup=setup_code2,number=10000))
print("dict:", timeit.timeit(using_dict_comp,setup=setup_code2,number=10000))

Output:

'set:', 0.0069959163665771484 
'dict:', 0.0006661415100097656

'set:', 0.6653687953948975  # this "fasteness" goes away as soon as more items are in the set
'dict:', 0.7533159255981445 

Upvotes: 2

Daniel Trugman
Daniel Trugman

Reputation: 8501

Just try:

r = sum(1 for val in v if c[val] == 'fruit')

Or, like @jpp suggested (a bit shorter):

r = sum(c[val] == 'fruit' for val in v)

Or, if you need speed, like @Patrick Artner:

ctag = { k: v for k, v in c.items() if v == 'fruit' }
r = sum(1 for val in v if val in ctag)

Explanation: Accessing the value and comparing each time is quite expensive. It is cheaper to create a new dictionary where all keys are fruits, and then simply ignore the values. This is better than using a set, because sets are implemented using balanced trees with search complexity of O(logN) whereas dicts are implemented using hashmaps with search complexity of O(1) as long as the number of the items in the map doesn't exceed the inner capacity by much...

Upvotes: 2

martineau
martineau

Reputation: 123453

Something short (similar to @Daniel Trugman's answer but slightly simpler):

r = sum(1 for value in c.values() if value == 'fruit')

Upvotes: 0

jpp
jpp

Reputation: 164653

Here's one solution:

res = sum(c[value] == 'fruit' for value in v)

This works because bool is treated as a subclass of int, i.e. Boolean True / False are equivalent to integers 1 / 0.

Upvotes: 0

Yugandhar Chaudhari
Yugandhar Chaudhari

Reputation: 3964

Try this first figure out type then find occurrences of keys of dictonary c

c = { 'banana': 'fruit', 'apple': 'fruit', 'pizza': 'fastfood', 'fries':'fastfood' }

v = [ 'fries', 'banana', 'banana', 'apple', 'pizza' ]

result = [c[i] for i in v] #this will iterate and give the type of element

output={}

for key in c.values(): output[key] =str(result.count(key))

print(output)

Upvotes: 0

Related Questions