update defaultdict count according to the occurrence of tuples in two lists

Viewed 401

I have two lists and would like to create a dictionary to record the occurrence of tuples.

My current code:

tup_to_find_test = [('good', 'pea'), ('leaf', 'sweet')] 
self_per_list_test = [('leaf', 'liquid'), ('leaf', 'sweet'), ('leaf', 'sweet'),('good', 'pea'),('good', 'pea'),('good', 'pea')]
from collections import defaultdict
tup_dict_test = defaultdict(int)
for tup_to_find_test in self_per_list_test:
   tup_dict_test[tup_to_find_test]+=1

My result is:

defaultdict(int, {('leaf', 'liquid'): 1, ('leaf', 'sweet'): 1, ('good', 'pea'): 3})

My desired result is:

('leaf', 'liquid'): 0, ('leaf', 'sweet'): 2, ('good', 'pea'): 3})

I do not know why the count of ('leaf', 'liquid') is 1. Isn't the default integer of defaultdict(int) zero? Why I got 1 for the ('leaf', 'liquid') tuple?

3 Answers

This line isn't doing what you think it is:

for tup_to_find_test in self_per_list_test:
   # ...

Here you are iterating a list elementwise, in this case the elements of self_per_list_test. There's no filtering taking place. As your for loop iterates, tup_to_find_test successively represents ('leaf', 'liquid'), ('leaf', 'sweet'), etc. The fact the name is the same as a variable you've defined earlier only serves to confuse.

Instead, you can use a ternary statement to differentiate operations:

for item in self_per_list_test:
    tup_dict_test[item] += 1 if item in tup_to_find_test else 0

print(tup_dict_test)

defaultdict(int, {('leaf', 'liquid'): 0, ('leaf', 'sweet'): 2, ('good', 'pea'): 3})

collections.Counter is more idiomatic with Python. It's good practice to use set for O(1) lookup within a dictionary comprehension.

from collections import Counter

tup_to_find_set = set(tup_to_find_test)
counts = Counter(self_per_list_test)

tup_dict_test = {k: v if k in tup_to_find_set else 0 for k, v in counts.items()}

print(tup_dict_test)

{('leaf', 'liquid'): 0, ('leaf', 'sweet'): 2, ('good', 'pea'): 3}

Isn't the default integer of defaultdict(int) zero?

Yes.

Why I got 1 for the ('leaf', 'liquid') tuple?

You wrote:

tup_dict_test[tup_to_find_test]+=1

That is, find the current value—which creates a new one set to zero—then add one to it and store the result back. The resulting value is 1.

Without reinventing the wheel. You can use counter from the wonderful collections standard module library for this.

from collections import Counter

tup_to_find_test = [('good', 'pea'), ('leaf', 'sweet')] 
self_per_list_test = [('leaf', 'liquid'), ('leaf', 'sweet'), ('leaf', 'sweet'),('good', 'pea'),('good', 'pea'),('good', 'pea')]

c = Counter(self_per_list_test)

for key in c:
    if key not in tup_to_find_test:
        c[key] = 0

print(c)

>>Counter({('good', 'pea'): 3, ('leaf', 'sweet'): 2, ('leaf', 'liquid'): 0})

Here we create a counter based on self_per_list_test and updates the counts to zero if it is not found in tup_to_find_test. Hope this is a more intuitive method in solving your problem.

Related