################################
### DICTIONARY DATA TYPE ###
################################
# A dictionary is like a list in that it's a collection of many values. But unlike indexes for lists, indexes for dictionaries can use
# different data types, not just integers. Indexes for dictionaries are called *keys*, and a key with its associated value is
# called a *key-value* pair. Dictionaries can also use interger values as keys, just like how lists use integers for indexes, but they
# do not have to satrt at 0 and can be any number.
myCat = {'size': 'big', 'color': 'grey', 'disposition': 'bitey', 123: 'value'}
myCat['size'] # result is 'big'
myCat[123] # result is 'value'
# Unlike lists, items in dictionaries are unordered. The first item in a list called myList would be list[0], but there is no 'first' item
# in a dictionary. While the order of times matters for determining whether two lists are the same, it does not matter in which order
# the key-value pairs are typed in a dicitonary.
firstList = ['cats', 'dogs', 'bats']
secondList = ['cats', 'bats', 'dogs']
firstList == secondList # result is False
firstDictionary = {'name': 'Whiskers', 'species': 'cat', 'age': '2'}
secondDictionary = {'species': 'cat', 'name': 'Whiskers', 'age': '2'}
firstDictionary == secondDictionary # result is True
# Since dictionaries are not ordered, they can't be sliced like lists. Also, trying to access a key that does not exist will result
# in a KeyError error message, much like a list's out-of-range IndexError error message.
# The ability to have arbitrary values for the keys allows you to organize data in powerful ways. For example, say you have a program to
# store data on your friends' birthdays. You can use a dictionary with the names as keys adn the birthdays as values. Input can also
birthdays = {'Alice': 'April 1', 'Sammy': 'March 2', 'Cameren': 'August 16'} # create dictionary
while True: # start infinite loop
print('Enter a name: (blank to quit)') # prompt user for input
name = input() # create name value, which will take value input by user
if name == "": # if empty string is entered, break the loop
break
if name in birthdays: # if name is in the birthdays dictionary, print the birthday
print(birthdays[name] + ' is the birthday of ' + name)
else: # if it's not in the birthday dictionary
print('I do not have birthday information for ' + name) # say the dictionary doesn't have the name
print('What is their birthday?') # ask what the birthday is
bday = input() # create variable bday which will hold the input from the user
birthdays[name] = bday # add key to birthdays using value currently held in name variable, assign it a value = to value in bday variable
print('Birthday database updated.') # print a confirmation message that hte dictionary has been updated. You can now enter the new name that was entered and you'll see that it has been added to the dictionary.
################################
### keys(), values(), and items() METHODS OF DICTIONARIES ###
################################
# There are three dictionary methods that return list-like values of the dictionary's keys, values, or both. These values are not true
# lists in that they cannot be modified and do not have an append() method. However, they can be used in for loops. The data types
# are called dict_keys, dict_values, and dict_items.
birthdays = {'Alice': 'April 1', 'Sammy': 'March 2', 'Cameren': 'August 16'}
for v in birthdays.keys():
print(v)
# Result is:
# April 1
# March 2
# August 16
for v in birthdays.values():
print(v)
# Result is:
# Alice
# Sammy
# Cameren
for v in birthdays.items():
print(v)
# Result is:
# ('Alice', 'April 1')
# ('Sammy', 'March 2')
# ('Cameren', 'August 16')
# Even though these are dict data types and aren't true lists, you can use the list function to return them as lists.
list(birthdays.items()) # Result is [('Alice', 'April 1'), ('Sammy', 'March 2'), ('Cameren', 'August 16')]
################################
### CHECKING WHETHER A KEY OR VALUE EXISTS IN A DICTIONARY ###
################################
# You can check whether a value or key is in a dictionary, just as you can in a list
birthdays = {'Alice': 'April 1', 'Sammy': 'March 2', 'Cameren': 'August 16'}
'Alice' in birthdays.keys() # result is True
'Rhoda' in birthdays.keys() # result is False
'March 2' in birthdays.values() # result is True
'February 1' not in birthdays.values() # result is True
################################
### THE get() METHOD ###
################################
# In the previous section, you had to check whether a key exists before accessing that key's value. The get() method allows you to check
# for the key and retrieve the value at the same time. It's set up like ISNULL() in SQL in that you specificy which key you'd like to retreive
# the value for, and then a value to insert if the key isn't found. Without this backup substitution value, Python would return an error.
shoppingList = {'apples': 5, 'pizzas': 2}
'I will bring ' + str(shoppingList.get('apples', 0)) + ' apples.' # result is 'I will bring 5 apples.'
'I will bring ' + str(shoppingList.get('hot dogs', 0)) + ' hot dogs.' # result is 'I will bring 0 hot dogs.' Notice how since the fallback value was used since 'hot dogs' doesn't exist as a key.
################################
### THE setdefault() METHOD ###
################################
# You'll often need to set a value in the dictionary for a key if the key does not already have a value. The code would look like:
shoppingList = {'apples': 5, 'pizzas': 2}
if 'hot dogs' not in shoppingList:
shoppingList['hot dogs'] = 4
shoppingList.items() # result is dict_items([('apples', 5), ('pizzas', 2), ('hot dogs', 4)])
# setdefault() is a shortcut around having to do multiple lines of code. The first argument is the key to check for, and the second argument is
# the value to set at that key if the key does not exist. It's a nice shortcut to ensure that a key exists.
shoppingList = {'apples': 5, 'pizzas': 2}
shoppingList.setdefault('hot dogs', 4) # 'hot dogs' does not exist, so it'll be created with a default value of 4
shoppingList.items() # result is dict_items([('apples', 5), ('pizzas', 2), ('hot dogs', 4)])
shoppingList.setdefault('apples', 200) # 'apples' already exists, so nothing will be changed with that keyword or value
shoppingList.items() # result is dict_items([('apples', 5), ('pizzas', 2), ('hot dogs', 4)])
# For a fuller example of how setdefault() can be a good shortcut, look at this program whichcounts the number of letters in the message.
# It creates an empty dictionary, starts a loop that goes through each letter of the message, and counts them. It creates keys in the
# dictionary for each new character it finds, and on a step if the character already exists, it takes the value and adds 1.
message = 'It was Christmas Eve and all through the house not a creature was stirring, not even a mouse.'
letterCount = {} # create empty dictionary
for character in message: # set a loop with variable character that goes through each character.
letterCount.setdefault(character,0) #check if that letter already exists, and if not, create a key with a default value of 0
letterCount[character] = letterCount[character] + 1 # add to the value of that key by one. If key was just created, it'll iterate from 0 to 1. If key already had value of 6, it'll iterate up to 7.
print(letterCount) # result is {'I': 1, 't': 8, ' ': 17, 'w': 2, 'a': 8, 's': 7, 'C': 1, 'h': 5, 'r': 6, 'i': 3, 'm': 2, 'E': 1, 'v': 2, 'e': 8, 'n': 5, 'd': 1, 'l': 2, 'o': 5, 'u': 4, 'g': 2, 'c': 1, ',': 1}
################################
### PRINTING DICTIONARIES ###
################################
# The pprint module is a good to way to 'pretty print' a dictionary's values.
import pprint
message = 'It was Christmas Eve and all through the house not a creature was stirring, not even a mouse.'
letterCount = {} # create empty dictionary
for character in message: # set a loop with variable character that goes through each character.
letterCount.setdefault(character,0) #check if that letter already exists, and if not, create a key with a default value of 0
letterCount[character] = letterCount[character] + 1 # add to the value of that key by one (values in dictionaries are accessed via square brackets, just like lists. If key was just created, it'll iterate from 0 to 1. If key already had value of 6, it'll iterate up to 7.
print(letterCount)
pprint.pprint(letterCount) # invoke the pprint method of the pprint module to print things nicely
# Result is:
# {' ': 17,
# ',': 1,
# '.': 1,
# 'C': 1,
# 'E': 1,
# 'I': 1,
# 'a': 8,
# 'c': 1,
# 'd': 1,
# 'e': 8,
# 'g': 2,
# 'h': 5,
# 'i': 3,
# 'l': 2,
# 'm': 2,
# 'n': 5,
# 'o': 5,
# 'r': 6,
# 's': 7,
# 't': 8,
# 'u': 4,
# 'v': 2,
# 'w': 2}
################################
### EXAMPLE OF TIC-TAC-TOE USING DICTIONARIES ###
################################
# In this example, a tic-tac-toe board can be represented using dictionary keys.
# 'top-L' - 'top-M' - 'top-R'
# ---------------------------------------
# 'mid-L' - 'mid-M' - 'mid-R'
# ---------------------------------------
# 'low-L' - 'low-M' - 'low-R'
# You can use string values to represent what's in each slot on the board: 'X', 'O', ' ' (a space character for nothing there)
# To start, create a dictionary for theBoard, placing a value of ' ' in each space (an empty board)
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
# You can then create a function that prints the current contents of the board so the players can see it.
# This function excepts the tic-tac-toe data structure to be a dictionary with keys for all nine slots, so you need to make sure that
# the dictionary passed to the function has all of the slots named correctly so that the match what the funciton is printing.
def printBoard(board): # define printBoard, with an argument board
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
print('-+-+-')
printBoard(theBoard) # invoke printBoard, passing in for the argument board the dictionary theBoard
# Result is an empty board:
# | |
# -+-+-
# | |
# -+-+-
# | |
# -+-+-
# If you assign values to any of the dicitonary keys, those will be printed by printBoard
theBoard = {'top-L': 'X', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'O'}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
print('-+-+-')
printBoard(theBoard)
# Result is a board with X's and O's entered based on the values for the keys:
# X| |
# -+-+-
# | |
# -+-+-
# | |O
# -+-+-
# You can then add code that allows the players to enter their moves
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
print('-+-+-')
turn = 'X' # variable to hold the name of the player whose turn it is
for i in range(9): # create loop that will go through 9 iterations
printBoard(theBoard) # print the board so the players can see the current state
print('Turn for ' + turn + '. Move on which space?') # identify whose turn it is and ask the player to make a move
move = input() # variable to hold the move that the player enteres
#next, sets the key value based on what the user places into the move variable. If user enters 'Top-M' and turn happens to be 'X', then
# the code becomes 'theBoard[top-M] = 'X''. This will assign the value to 'X' for the topBoard dictionary key 'top-M'
theBoard[move] = turn
if turn == 'X': # switch the turn (flip the turn variable to the other player's character)
turn = 'O'
else:
turn = 'X'
printBoard(theBoard) # one it completes nine rounds, the loop will stop and the program will display the final result
################################
### NESTED DICTIONARIES AND LISTS ###
################################
# Dictionaries and lists can contain other dicitonaries and lists. As an example, there is a program that has nested dictionaries
# to show customers that each have their own owners. numOrders then totals the orders
# create allCustomers dictionaries, whose keys 'Alice', 'Carol', and 'Chris' each have nested dictionaries
allCustomers = {'Alice': {'apples': 5, 'pretzels': 12},
'Carol': {'guitar': 12, 'speakers': 2},
'Chris': {'apples': 2, 'pizzas': 7, 'toaster': 1}}
def totalOrders(customers, item):
numOrders = 0
# within funciton, loop iterates over all of the key-value pairs plugged into customers argument
# in the loop, customer name is assigned to k, and the dictionary of items is assigned to v.
for k, v in customers.items():
numOrders = numOrders + v.get(item,0) # count the number of whatever item is passed in the item argument for this iteration of the loop
return numOrders # return the value out of the function
print('Number of orders:')
print(' - Apples '+ str(totalOrders(allCustomers, 'apples'))) # run totalOrders, plugging in dictionary allCustomers, looking for item 'apples', result is 7
print(' - Hot Dogs '+ str(totalOrders(allCustomers, 'hot dogs'))) # run totalOrders, plugging in dictionary allCustomers, looking for item 'apples', result is o
print(' - Pretzels '+ str(totalOrders(allCustomers, 'pretzels')))
print(' - Guitar '+ str(totalOrders(allCustomers, 'guitar')))
print(' - Speakers '+ str(totalOrders(allCustomers, 'speakers')))
print(' - Cat Food '+ str(totalOrders(allCustomers, 'cat food')))
print(' - Pizzas '+ str(totalOrders(allCustomers, 'pizzas')))
print(' - Toasters '+ str(totalOrders(allCustomers, 'toaster')))
print(' - Computers '+ str(totalOrders(allCustomers, 'computer')))
################################
### DICTIONARY COMPREHENSION ###
################################
# comprehension allows you concisely form a new list, set, or dictionary by filtering the elements of a collection
# A dictionary comprehension has a format of [key expr : value expr for value in collection if condition]
animals = ['bat', 'cat', 'really big snake', 'parrot']
# mapping the index position of the list as the key, and the item in the list as a value
myMappedDictionary = {index: value for index, value in enumerate(animals)}
myMappedDictionary # result is {0: 'bat', 1: 'cat', 2: 'really big snake', 3: 'parrot'}
# example using an optional filter
myMappedDictionary = {index: value for index, value in enumerate(animals) if len(value) > 3}
myMappedDictionary # result is {2: 'really big snake', 3: 'parrot'}
################################
### CREATING DICTIONARIES FROM SEQUENCES ###
################################
# it's common to end up with two sequences that you want to place into a dictionary as key-value pairs.
# Since a dictionary is basically a collection of tuples, you can supply them into a dictionary using zip
sequence1 = ['cat', 'mouse', 'parrot']
sequence2 = ['one', 'two', 'three', 'four']
myDictionary = {}
myDictionary = dict(zip(sequence2, sequence1))
myDictionary # result is {'one': 'cat', 'two': 'mouse', 'three': 'parrot'}