# Data Structures
#   Data structures hold sets of data in various formats
#   We will discuss: list, dictionary (dict), tuple, set

# List
# Lists have collections of data
# which are indexed linerally (i.e. 0, 1, 2, 3)
# with elements separated by commas
# are indexed starting at 0
# contained within square brackets

# Declare our list
letters = ['a', 'g', 'h', 'l', 'p', 't', 'w']
# Add an element
letters.append('z')
# Get rid of a matching item from the list
letters.remove('l')
# Print out the contents of the list
print("Letters:", end='')
for letter in letters:
    print(f" {letter}", end='')
print()
# Reverse it and print it again with indexes
letters.reverse()
print("Letters Reversed:", end='')
for index, letter in enumerate(letters):
    print(f" ({index}){letter}", end='')
print()
# The number of elements in a list can be accessed with len()
print("The number of letters is:", len(letters))

# Dictionary
# Dictionaries have collections of data
# which are pairs of keys and values
# with element pairs separated by commas
# contained within curly braces
# Note: Keys in a dictionary must be unique

# Declare our dictionary
states = {"OH": "Ohio", "PA": "Pennsylvania", "CT": "Connecticut", "WV": "West Virginia", "MD": "Maryland"}
print("My States:", states)
# In order to sort the dictionary we have to pull out the keys,
# sort those, and then redeclare the dictionary ordered by the
# newly sorted list of keys
my_keys = list(states.keys())
my_keys.sort()
states = {i: states[i] for i in my_keys}
print("My States Sorted by Key:", states)
# The number of key/value pairs in a dictionary can be accessed with len()
print("The number of states is:", len(states))

# Tuple
# Tuples have collections of data
# typically of the same type, but does not have to be
# with elements separated by commas
# are indexed starting at 0
# contained within parentheses
# Note: Once a tuple has been created its
#       values cannot be changed,
#       and elements cannot be added/removed

# Declare our tuple
position = (422, 915)
print(f"Position (x, y) is: {position[0]}, {position[1]}")
# The number of elements in a tuple ca be accessed with len()
print("The number of elements in the position tuple is:", len(position))
color = (88, 199, 213)
print(f"Color tuple: R={color[0]} G={color[1]} B={color[2]}")

# Always keep in mind that the elements of lists and tuples,
# and the values of dictionary key/value pairs can be of
# nearly any type.
# For example, they can be (but are not limited to):
#    strings: "Cheese"
#    integers: 44
#    lists: ['a', 'b', 'c']
#    objects: MyClass(param1, param2)

# Set
# Sets have collections of data
# set elements must be unique in the set
# Usually of the same type because uniqueness makes less sense otherwise
# with elements separated by commas
# where elements are unordered (i.e. can't reference specific element, slice, or index)
# are contained within curly braces
# Note: Once a set has been created its values cannot be changed,
#       and elements cannot be added/removed

# Declare our set
dice = {"1", "2", "3", "4", "5", "6"}
# Testing if an element exists is done with "in"
if "3" in dice:
    print("The dice set contains a 3!")
# Iterate over a set
for die in dice:
    print(f"dice element: {die}")
# NOTE!! You will see that the printed elements are in an arbitrary order.
#        Because sets are unordered you cannot rely on which element comes
#        before or after another element and will likely not match the order
#        created inside the set declaration.