Start learning free

LESSON 255 OF 410 · HASH TABLE

Hash Maps in Python

Lesson Read the full explanation

Hash Tables, better known as Hash Maps, are just collections of key-value pairs. In other words, they are pieces of data (values) mapped to unique identifiers called properties (keys).

Hash Maps were designed to give us a way of, given a key, associating a value with it for very quick lookups.

In Python, the Hash Map is implemented as a dictionary. This is why adding and lookup in a dictionary is so fast.

If you've worked with dictionaries in the past, you were working with Hash Maps without knowing it.


Creation:

You create an empty Hash Map just by creating an empty dictionary, like this:

hashMap = {}

Creating an empty dictionary takes O(1) time.


Initialization:

You can also add some key-value pairs in your dictionary at creation:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown'
}

Accessing values:

A value is retrieved from a dictionary by specifying its corresponding key in square brackets ([]):

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown'
}

# Accessing values:
print(person['name']); # "Andy"
print(person['age']); # 30

# Accessing non-existing key:
print(person['job']); # raises a KeyError exception

As you can see, if you refer to a key that is not in the dictionary, Python raises a KeyError exception.

Accessing a value from a dictionary takes O(1) time.


Adding an entry:

Adding an entry to an existing dictionary is simply a matter of assigning a new key and value, via the assignment operator:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown'
}

# Adding new entries:
person['job'] = 'Teacher'

str = 'hobby'
person[str] = 'Fishing'

print(person)
# The new Hash Map:
# {
#	'name': 'Andy',
#	'age': 30,
#	'hair color': 'brown',
#	'job': 'Teacher',
#	'hobby': 'Fishing'
# }

Adding a new entry to a dictionary takes O(1) time.


Updating an entry:

If you want to update an entry, you can just assign a new value to an existing key:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown'
}

# Updating entries:
person['name'] = 'Andrew'
person['age'] = 29

print(person)
# The new Hash Map:
# {
#	'name': 'Andrew',
#	'age': 29,
#	'hair color': 'brown'
# }

Updating an entry from a dictionary takes O(1) time.


Deleting entries:

The del operator allows you to remove a key from a dictionary:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown',
	'hobby': 'Fishing'
}

# Deleting entries:
del person['name']

key = 'age'
del person[key]

# Deleting a non-existent entry:
# del person['job'] -> this would raise a KeyError exception

print(person)
# The new Hash Map:
# {
#	'hair color': 'brown',
#	'hobby': 'Fishing'
# }

If you try to delete a key that is not in the dictionary, Python raises a KeyError exception.

Deleting an entry from a dictionary takes O(1) time.


Checking if a key exists:

You can check if a key exists in a dictionary using the in operator:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown',
	'hobby': 'Fishing'
}

key = 'age'
print(key in person) # True

print('job' in person) # False

Iterating over the dictionary keys:

If we want to iterate over keys of the dictionary, we can use the for loop along with the keys() method:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown',
	'hobby': 'Fishing'
}

for key in person.keys():
    print(key + ' is ' + str(person[key]))

# This will print the following:
# hobby is Fishing
# age is 30
# hair color is brown
# name is Andy

Notice the order is not the same as initiated. Dictionary keeps the data in random order

Also Notice that we used str() to cast / convert the value into a string since we have the integer 30 which needs casting. If we don't use it, we will get a runtime error

Iterating over a dictionary takes O(n) time.


Iterating over the dictionary values:

If we want to iterate over values of the dictionary, we can use the for loop along with the values() method:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown',
	'hobby': 'Fishing'
}

for val in person.values():
    print(val)

# This will print the following:
# Fishing
# 30
# brown
# Andy

Notice the order is not the same as initiated. Dictionary keeps the data in random order

Iterating over a dictionary takes O(n) time.


Iterating using both the key and the value for simplicity:

If we want to iterate over both keys and values of the dictionary, we can use the for loop along with the items() method:

person = {
	'name': 'Andy',
	'age': 30,
	'hair color': 'brown',
	'hobby': 'Fishing'
}

for key, val in person.items():
    print(key + ' is ' +  str(val))

# This will print the following:
# hobby is Fishing
# age is 30
# hair color is brown
# name is Andy

Notice the order is not the same as initiated. Dictionary keeps the data in random order

Also Notice that we used str() to cast / convert the value into a string since we have the integer 30 which needs casting. If we don't use it, we will get a runtime error

Iterating over a dictionary takes O(n) time.


Assignment
Follow the Coding Tutorial and let's play with some dictionaries.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 7
    Ctrl + Enter to check

    PYTHON · HASH TABLE

    Hash Map in Python, explained

    Introduction

    In this lesson, we will explore the concept of Hash Maps in Python, which are implemented as dictionaries. Hash Maps are essential data structures that allow for efficient storage and retrieval of data using key-value pairs. They are widely used in various programming scenarios, such as caching, indexing, and managing configurations.

    Understanding the Basics

    Hash Maps, or dictionaries in Python, are collections of key-value pairs. Each key is unique and maps to a specific value. This structure allows for quick lookups, additions, and deletions. Understanding the basics of Hash Maps is crucial before diving into more complex operations.

    For example, consider a dictionary that stores information about a person:

    person = {
        'name': 'Andy',
        'age': 30,
        'hair color': 'brown'
    }
    

    In this dictionary, 'name', 'age', and 'hair color' are keys, and 'Andy', 30, and 'brown' are their corresponding values.

    Main Concepts

    Let's delve into the key concepts and techniques involved in working with Hash Maps:

    • Creation: Creating an empty dictionary takes O(1) time.
    • Initialization: You can initialize a dictionary with key-value pairs.
    • Accessing Values: Retrieve values using their corresponding keys.
    • Adding Entries: Add new key-value pairs to the dictionary.
    • Updating Entries: Update the value of an existing key.
    • Deleting Entries: Remove key-value pairs using the del operator.
    • Checking Key Existence: Use the in operator to check if a key exists.
    • Iterating: Iterate over keys, values, or both using loops.

    Examples and Use Cases

    Let's explore some examples to demonstrate the use of Hash Maps in various contexts:

    # Example 1: Creating and initializing a dictionary
    person = {
        'name': 'Andy',
        'age': 30,
        'hair color': 'brown'
    }
    
    # Example 2: Accessing values
    print(person['name'])  # Output: Andy
    print(person['age'])   # Output: 30
    
    # Example 3: Adding and updating entries
    person['job'] = 'Teacher'
    person['age'] = 31
    
    # Example 4: Deleting entries
    del person['hair color']
    
    # Example 5: Checking key existence
    print('name' in person)  # Output: True
    print('hair color' in person)  # Output: False
    
    # Example 6: Iterating over keys and values
    for key, value in person.items():
        print(f"{key}: {value}")
    

    Common Pitfalls and Best Practices

    When working with Hash Maps, it's important to avoid common mistakes and follow best practices:

    • Avoid KeyErrors: Always check if a key exists before accessing its value.
    • Use Meaningful Keys: Choose descriptive and meaningful keys for better readability.
    • Optimize Performance: Use Hash Maps for quick lookups and avoid unnecessary iterations.

    Advanced Techniques

    Advanced techniques can enhance the functionality of Hash Maps:

    • Default Values: Use the get() method to provide default values for non-existent keys.
    • Nested Dictionaries: Create dictionaries within dictionaries for complex data structures.
    • Comprehensions: Use dictionary comprehensions for concise and efficient dictionary creation.
    # Example of using get() method
    print(person.get('hobby', 'Not specified'))  # Output: Not specified
    
    # Example of nested dictionaries
    person['address'] = {'city': 'New York', 'zip': '10001'}
    
    # Example of dictionary comprehension
    squares = {x: x*x for x in range(6)}
    print(squares)  # Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
    

    Code Implementation

    Here is a comprehensive code implementation demonstrating the correct use of Hash Maps:

    # Creating and initializing a dictionary
    person = {
        'name': 'Andy',
        'age': 30,
        'hair color': 'brown'
    }
    
    # Accessing values
    print(person['name'])  # Output: Andy
    print(person['age'])   # Output: 30
    
    # Adding and updating entries
    person['job'] = 'Teacher'
    person['age'] = 31
    
    # Deleting entries
    del person['hair color']
    
    # Checking key existence
    print('name' in person)  # Output: True
    print('hair color' in person)  # Output: False
    
    # Iterating over keys and values
    for key, value in person.items():
        print(f"{key}: {value}")
    
    # Using get() method
    print(person.get('hobby', 'Not specified'))  # Output: Not specified
    
    # Nested dictionaries
    person['address'] = {'city': 'New York', 'zip': '10001'}
    
    # Dictionary comprehension
    squares = {x: x*x for x in range(6)}
    print(squares)  # Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
    

    Debugging and Testing

    Debugging and testing are crucial for ensuring the correctness of your code:

    • Debugging: Use print statements or debugging tools to trace and fix issues.
    • Testing: Write test cases to validate the functionality of your Hash Map operations.
    # Example test cases
    def test_person_dict():
        person = {
            'name': 'Andy',
            'age': 30,
            'hair color': 'brown'
        }
        assert person['name'] == 'Andy'
        assert person['age'] == 30
        person['job'] = 'Teacher'
        assert person['job'] == 'Teacher'
        del person['hair color']
        assert 'hair color' not in person
    
    test_person_dict()
    print("All tests passed!")
    

    Thinking and Problem-Solving Tips

    When working with Hash Maps, consider the following tips:

    • Break Down Problems: Divide complex problems into smaller, manageable parts.
    • Practice: Solve coding exercises and projects to improve your skills.
    • Explore: Experiment with different use cases and scenarios to deepen your understanding.

    Conclusion

    In this lesson, we covered the fundamental concepts of Hash Maps in Python, including creation, initialization, accessing values, adding and updating entries, deleting entries, checking key existence, and iterating over dictionaries. We also discussed common pitfalls, best practices, advanced techniques, and provided a comprehensive code implementation. Mastering these concepts will enhance your ability to work with Hash Maps effectively in various programming scenarios.

    Additional Resources

    For further reading and practice, consider the following resources:

    Keep going with PythonAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free