What is a Hashtable/Hashmap?

A hashtable is a data structure that with a collection of key-value pairs, where each key maps to a value, and the keys must be unique and hashable.

  • In Python there is a built in hashtable known as a dictionary.

The primary purpose of a hashtable is to provide efficient lookup, insertion, and deletion operations. When an element is to be inserted into the hashtable, a hash function is used to map the key to a specific index in the underlying array that is used to store the key-value pairs. The value is then stored at that index. When searching for a value, the hash function is used again to find the index where the value is stored.

The key advantage of a hashtable over other data structures like arrays and linked lists is its average-case time complexity for lookup, insertion, and deletion operations.

  • The typical time complexity of a hashtable is O(1).

What is Hashing and Collision?

Hashing is the process of mapping a given key to a value in a hash table or hashmap, using a hash function. The hash function takes the key as input and produces a hash value or hash code, which is then used to determine the index in the underlying array where the value is stored. The purpose of hashing is to provide a quick and efficient way to access data, by eliminating the need to search through an entire data structure to find a value.

However, it is possible for two different keys to map to the same hash value, resulting in a collision. When a collision occurs, there are different ways to resolve it, depending on the collision resolution strategy used.

Python's dictionary implementation is optimized to handle collisions efficiently, and the performance of the dictionary is generally very good, even in the presence of collisions. However, if the number of collisions is very high, the performance of the dictionary can degrade, so it is important to choose a good hash function that minimizes collisions when designing a Python dictionary.

What is a Set?

my_set = set([1, 2, 3, 2, 1])
print(my_set)  

# What do you notice in the output?
# The set that was outputted was 1, 2, 3
# No duplicates

# Why do you think Sets are in the same tech talk as Hashmaps/Hashtables?
# They are going to have unique values, so no duplicates are present
#
{1, 2, 3}

Dictionary Example

Below are just some basic features of a dictionary. As always, documentation is always the main source for all the full capablilties.

lover_album = {
    "title": "Lover",
    "artist": "Taylor Swift",
    "year": 2019,
    "genre": ["Pop", "Synth-pop"],
    "tracks": {
        1: "I Forgot That You Existed",
        2: "Cruel Summer",
        3: "Lover",
        4: "The Man",
        5: "The Archer",
        6: "I Think He Knows",
        7: "Miss Americana & The Heartbreak Prince",
        8: "Paper Rings",
        9: "Cornelia Street",
        10: "Death By A Thousand Cuts",
        11: "London Boy",
        12: "Soon You'll Get Better (feat. Dixie Chicks)",
        13: "False God",
        14: "You Need To Calm Down",
        15: "Afterglow",
        16: "Me! (feat. Brendon Urie of Panic! At The Disco)",
        17: "It's Nice To Have A Friend",
        18: "Daylight"
    }
}

# What data structures do you see?
# Lists [square brackets are a list] and dictionaries {curvy brackets are a dictionary}
#

# Printing the dictionary
print(lover_album)
{'title': 'Lover', 'artist': 'Taylor Swift', 'year': 2019, 'genre': ['Pop', 'Synth-pop'], 'tracks': {1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight'}}
print(lover_album.get('tracks'))
# or
print(lover_album['tracks'])
{1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight'}
{1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight'}
print(lover_album.get('tracks')[4])
# or
print(lover_album['tracks'][4])
The Man
The Man
lover_album["producer"] = set(['Taylor Swift', 'Jack Antonoff', 'Joel Little', 'Taylor Swift', 'Louis Bell', 'Frank Dukes'])

# What can you change to make sure there are no duplicate producers?
# Make it a set by adding set and parenthesis 
#

# Printing the dictionary
print(lover_album)
{'title': 'Lover', 'artist': 'Taylor Swift', 'year': 2019, 'genre': ['Pop', 'Synth-pop'], 'tracks': {1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight'}, 'producer': {'Jack Antonoff', 'Taylor Swift', 'Frank Dukes', 'Joel Little', 'Louis Bell'}}
lover_album["tracks"].update({19: "All Of The Girls You Loved Before"})

# How would add an additional genre to the dictionary, like electropop? 
# Change tracks to genre and what number genre it is and put "electropop" instead of "All Of The Girls You Loved Before".
# 

# Printing the dictionary
print(lover_album)
{'title': 'Lover', 'artist': 'Taylor Swift', 'year': 2019, 'genre': ['Pop', 'Synth-pop'], 'tracks': {1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight', 19: 'All Of The Girls You Loved Before'}, 'producer': {'Jack Antonoff', 'Taylor Swift', 'Frank Dukes', 'Joel Little', 'Louis Bell'}}
for k,v in lover_album.items(): # iterate using a for loop for key and value
    print(str(k) + ": " + str(v))
title: Lover
artist: Taylor Swift
year: 2019
genre: ['Pop', 'Synth-pop']
tracks: {1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight', 19: 'All Of The Girls You Loved Before'}
producer: {'Jack Antonoff', 'Taylor Swift', 'Frank Dukes', 'Joel Little', 'Louis Bell'}
def search():
    search = input("What would you like to know about the album?")
    if lover_album.get(search.lower()) == None:
        print("Invalid Search")
    else:
        print(lover_album.get(search.lower()))

search()

# This is a very basic code segment, how can you improve upon this code?
#  Something that I can do to improve upon it is add more to the search such as after I type in tracks, it asks me for a certain track.
#
{1: 'I Forgot That You Existed', 2: 'Cruel Summer', 3: 'Lover', 4: 'The Man', 5: 'The Archer', 6: 'I Think He Knows', 7: 'Miss Americana & The Heartbreak Prince', 8: 'Paper Rings', 9: 'Cornelia Street', 10: 'Death By A Thousand Cuts', 11: 'London Boy', 12: "Soon You'll Get Better (feat. Dixie Chicks)", 13: 'False God', 14: 'You Need To Calm Down', 15: 'Afterglow', 16: 'Me! (feat. Brendon Urie of Panic! At The Disco)', 17: "It's Nice To Have A Friend", 18: 'Daylight', 19: 'All Of The Girls You Loved Before'}

Hacks

  • Answer ALL questions in the code segments
  • Create a venn diagram or other compare and contrast tool related to hashmaps.
    • What are the pro and cons of using this data structure?
      • Hashmaps are a fast and flexible data structure used for storing key-value pairs. They offer efficient retrieval and insertion of data with constant time complexity, and support any data type including custom objects. Additionally, hashmaps are easy to implement and widely used in many programming languages. However, there are also some potential drawbacks, including the possibility of collisions, increased memory usage, and slower iteration through all the keys or values.
    • Dictionary vs List
  • Expand upon the code given to you, possible improvements in comments
  • Build your own album showing features of a python dictionary

  • For Mr. Yeung's class: Justify your favorite Taylor Swift song, answer may effect seed

    • My favorite Taylor Swift song is Love Story. This is because it is a banger and very fun and easy to sing along with. The chorus is fire fr fr.
adele21_album = {
    "title": "adele21",
    "artist": "Adele",
    "year": 2019,
    "genre": ["Pop", "Synth-pop"],
    "tracks": {
        1: "Rolling in the Deep",
        2: "Rumour Has It",
        3: "Turning Tables",
        4: "Don't You Remember",
        5: "Set Fire to the Rain",
        6: "He Won't Go",
        7: "Take It All",
        8: "I'll Be Waiting",
        9: "One and Only",
        10: "Lovesong"
    }
}

# What data structures do you see?
# Lists [square brackets are a list] and dictionaries {curvy brackets are a dictionary}
#

# Printing the dictionary
print(adele21_album)
{'title': 'adele21', 'artist': 'Adele', 'year': 2019, 'genre': ['Pop', 'Synth-pop'], 'tracks': {1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong'}}
print(adele21_album.get('tracks'))
# or
print(adele21_album['tracks'])
{1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong'}
{1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong'}
print(adele21_album.get('tracks')[5])
# or
print(adele21_album['tracks'][5])
Set Fire to the Rain
Set Fire to the Rain
adele21_album["producer"] = set(['Adele Adkins', 'Jim Abbiss', 'Paul Epworth', 'Rick Rubin', 'Fraser T Smith', 'Adele Adkins', 'Ryan Tedder', 'Dan Wilson'])
# What can you change to make sure there are no duplicate producers?
# Make it a set by adding set and parenthesis 
#

# Printing the dictionary
print(adele21_album)
{'title': 'adele21', 'artist': 'Adele', 'year': 2019, 'genre': ['Pop', 'Synth-pop'], 'tracks': {1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong'}, 'producer': {'Paul Epworth', 'Rick Rubin', 'Dan Wilson', 'Jim Abbiss', 'Ryan Tedder', 'Adele Adkins', 'Fraser T Smith'}}
adele21_album["tracks"].update({11: "Someone Like You"})

# How would add an additional genre to the dictionary, like electropop? 
# Change tracks to genre and what number genre it is and put "electropop" instead of "All Of The Girls You Loved Before".
# 

# Printing the dictionary
print(adele21_album)
{'title': 'adele21', 'artist': 'Adele', 'year': 2019, 'genre': ['Pop', 'Synth-pop'], 'tracks': {1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong', 11: 'Someone Like You'}, 'producer': {'Paul Epworth', 'Rick Rubin', 'Dan Wilson', 'Jim Abbiss', 'Ryan Tedder', 'Adele Adkins', 'Fraser T Smith'}}
for k,v in adele21_album.items(): # iterate using a for loop for key and value
    print(str(k) + ": " + str(v))
title: adele21
artist: Adele
year: 2019
genre: ['Pop', 'Synth-pop']
tracks: {1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong', 11: 'Someone Like You'}
producer: {'Paul Epworth', 'Rick Rubin', 'Dan Wilson', 'Jim Abbiss', 'Ryan Tedder', 'Adele Adkins', 'Fraser T Smith'}
def search():
    search = input("What would you like to know about the album?")
    if adele21_album.get(search.lower()) == None:
        print("Invalid Search")
    else:
        print(adele21_album.get(search.lower()))

search()
{1: 'Rolling in the Deep', 2: 'Rumour Has It', 3: 'Turning Tables', 4: "Don't You Remember", 5: 'Set Fire to the Rain', 6: "He Won't Go", 7: 'Take It All', 8: "I'll Be Waiting", 9: 'One and Only', 10: 'Lovesong', 11: 'Someone Like You'}
import sqlite3

# define the database connection and cursor
conn = sqlite3.connect('song_opinions.db')
c = conn.cursor()

# create a table to store the song opinions if it doesn't exist
c.execute('''CREATE TABLE IF NOT EXISTS song_opinions 
             (title text, opinion text, rating integer)''')

# define a function to search for a song and add an opinion and rating
def add_opinion():
    # ask the user for the song title, their opinion, and their rating
    title = input("Enter the title of the song: ")
    opinion = input("Enter your opinion of the song: ")
    rating = int(input("Enter your rating out of 10: "))

    # search for the song in the album
    if title in adele21_album['tracks'].values():
        # insert the title, opinion, and rating into the database
        c.execute("INSERT INTO song_opinions VALUES (?, ?, ?)", (title, opinion, rating))
        conn.commit()
        print("Opinion added to the database!")
    else:
        print("Song not found in album.")

# call the function to add an opinion
add_opinion()

# close the database connection
conn.close()
Opinion added to the database!
def delete_opinion():
    # define the database connection and cursor
    conn = sqlite3.connect('song_opinions.db')
    c = conn.cursor()
    
    # ask the user for the title of the song to delete
    title = input("Enter the title of the song to delete: ")
    
    # delete the row corresponding to the specified song title
    c.execute("DELETE FROM song_opinions WHERE title=?", (title,))
    conn.commit()
    
    # check if any rows were deleted
    if c.rowcount == 0:
        print("No opinions found for that song title.")
    else:
        print(f"{c.rowcount} opinion(s) deleted from the database.")
    
    # close the database connection
    conn.close()

# call the function to delete an opinion
delete_opinion()
1 opinion(s) deleted from the database.