
# The "defaultdict" class is very suitable for the follow problem:
#
# 		count the words in a document. 
#
# An obvious approach is to create a dictionary in which:
#
#          the keys are words and 
#          the values are counts. 
#
# Example: { "the": 10, "in": 2, ... }

# (1) Straight forward code in Python:

input = [ 'the', 'man', 'owns', 'the', 'hat']

word_counts = {}

for word in input:
    if word in word_counts:
        word_counts[word] += 1
    else:
        word_counts[word] = 1

print(word_counts)

# (2) Using the “forgiveness is better than permission” approach:
#
#       handle the exception from trying to look up a missing key
#

word_counts = {}

for word in input:
    try:
        word_counts[word] += 1		# Found, increment
    except:
        word_counts[word] = 1		# Not found: first occurence

print(word_counts)


# ##########################################################################
# Because this is a common application, Pythan has created
# a special class in its library:
#
#    Library module: collection
#    Class name:     defaultdict
#
# A defaultdict is like a regular dictionary, except that:
#
#     when you try to look up a key it doesn’t contain, 
#     it **first** adds a value for it using a "zero-argument function"
#     that you provided when you created it. 
#
# #########################################################################

from collections import defaultdict


print("Value of int() = ", int() )

word_counts = defaultdict(int) 		# We provide the zero-arg
					# function "int()"
					# This function produces 0

print("word_counts at start: ", word_counts)

for word in input:
    word_counts[word] += 1

print("word_counts at end: ", word_counts)


# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
# Experiment
# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

def my_func():
    """ Zero argument function that returns 1 """
    return 1

word_counts = defaultdict(my_func)      # We provide the zero-arg
                                        # function "my_func()" 
                                        # This function produces 1

print("EXPERIMENT: word_counts at start: ", word_counts)

for word in input:
    word_counts[word] += 1

print("EXPERIMENT: word_counts at end: ", word_counts)



# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# defaultdict class can also be used with a list.
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

print("\n\n*** defaultdict with LISTS:")

# Fact 1:  the list() function returns an EMPTY list

dd_list = defaultdict(list) 	# list() produces an empty list

dd_list[2].append(1)		# now dd_list contains {2: [1]}
print(dd_list)
		# Explanation:
		#	Initially:	dd_list = []
		#
		#	Since dd_list[2] does not exists: 
		#
		#		execs:   dd_list[2] = list()
		#
		#		Result:	 dd_list = {2: []}
		#
		#	Finally: runs
		#
		#		dd_list[2].append(1)
		#
		#		Result:   dd_list = {2: [1]}


dd_list[2].append(6)		# now dd_list contains {2: [1,6]}
print(dd_list)
		# Explanation:


# More advanced form

dd_dict = defaultdict(dict) # dict() produces an empty dict
dd_dict["Joel"]["City"] = "Seattle" 	# { "Joel" : { "City" : Seattle"}}

print(dd_dict)


# Create your OWN list creation function

# We use a zero-argument (nameless) function  that returns the list [0,0]

dd_pair = defaultdict(lambda: [0, 0])

dd_pair[4] = 9
print(dd_pair)

                # Explanation:
                #       Initially:      dd_pair = []
                #
                #       Since dd_pair[4] does not exists:
                #
                #               execs:   dd_pair[4] = [0, 0]
                #
                #               Result:  dd_pair {4: [0,0]}
                #
                #       Finally: runs
                #
                #               dd_pair[4] = 9
                #
                #               Result:   dd_pair = {4: 9}


#########################################################################
# defaultdict is useful when:
#
#   we’re using dictionaries to “collect” results by some key and
#   don’t want to have to check every time to see if the key exists yet.
#########################################################################


