w3resource

Two-Way Python Dictionary: Building a Bidirectional Map with Equal Efficiency


87. Bidirectional Dictionary

Write a Python program to implement a bidirectional dictionary (BiDict) class that maintains one-to-one mappings and allows lookup in both directions with equal efficiency. Ensure that when a key-value pair is added, any existing entries with the same key or value are removed.

Solution:

Python Code:

# Define a class for a bidirectional dictionary (BiDict) that supports one-to-one mappings.
class BiDict:
    """
    A bidirectional dictionary that maintains one-to-one mappings
    and allows lookup in both directions with equal efficiency.
    """
    # Initialize the BiDict with two dictionaries: forward (key -> value) and backward (value -> key).
    def __init__(self):
        self.forward = {}  # Stores the mapping from keys to values.
        self.backward = {}  # Stores the mapping from values to keys.
    
    # Define the behavior for setting a key-value pair using the `[]` operator.
    def __setitem__(self, key, value):
        """Set a key-value pair, removing any existing entries with the same key or value."""
        # If the key already exists, remove its current mapping from both forward and backward dictionaries.
        if key in self.forward:
            old_value = self.forward[key]
            del self.backward[old_value]  # Remove the old value from the backward mapping.
            del self.forward[key]  # Remove the old key from the forward mapping.
        
        # If the value already exists, remove its current mapping from both forward and backward dictionaries.
        if value in self.backward:
            old_key = self.backward[value]
            del self.forward[old_key]  # Remove the old key from the forward mapping.
            del self.backward[value]  # Remove the old value from the backward mapping.
        
        # Add the new key-value pair to both forward and backward dictionaries.
        self.forward[key] = value
        self.backward[value] = key
    
    # Define the behavior for getting the value associated with a key using the `[]` operator.
    def __getitem__(self, key):
        """Get the value for a key."""
        return self.forward[key]  # Retrieve the value from the forward dictionary.
    
    # Define a method to get the key associated with a value.
    def get_key(self, value):
        """Get the key for a value."""
        return self.backward[value]  # Retrieve the key from the backward dictionary.
    
    # Define the behavior for deleting a key-value pair using the `del` operator.
    def __delitem__(self, key):
        """Remove a key-value pair by key."""
        value = self.forward[key]  # Get the value associated with the key.
        del self.forward[key]  # Remove the key from the forward dictionary.
        del self.backward[value]  # Remove the value from the backward dictionary.
    
    # Define the behavior for checking if a key exists in the BiDict.
    def __contains__(self, key):
        """Check if a key exists."""
        return key in self.forward  # Check if the key is in the forward dictionary.
    
    # Define the behavior for getting the number of key-value pairs in the BiDict.
    def __len__(self):
        """Get the number of pairs."""
        return len(self.forward)  # Return the length of the forward dictionary.
    
    # Define the string representation of the BiDict for debugging or printing.
    def __repr__(self):
        """String representation."""
        return f"BiDict({self.forward})"  # Represent the BiDict using its forward dictionary.

# Example usage of BiDict

# Create an instance of BiDict.
bidict = BiDict()

# Set key-value pairs in the bidirectional dictionary.
bidict['one'] = 1
bidict['two'] = 2
bidict['three'] = 3

# Forward lookup: Retrieve the value associated with the key 'one'.
print(bidict['one'])  # Output: 1

# Backward lookup: Retrieve the key associated with the value 2.
print(bidict.get_key(2))  # Output: 'two'

# Test the one-to-one constraint by assigning a new key to an existing value.
# This should remove the previous mapping ('one': 1) because the value 1 is reused.
bidict['four'] = 1

# Check if the key 'one' still exists in the BiDict after the reassignment.
print('one' in bidict)  # Output: False

# Perform a backward lookup to confirm the new mapping for the value 1.
print(bidict.get_key(1))  # Output: 'four'


Output:

1
two
False
Four

Explanation of Each Line:

  • Class Definition : Defines a BiDict class to implement a bidirectional dictionary with one-to-one mappings.
  • Docstring : Provides a description of the class and its functionality.
  • Initialization : Initializes the BiDict with two dictionaries: forward for key-to-value mappings and backward for value-to-key mappings.
  • Set Item Behavior : Implements the __setitem__ method to set key-value pairs while ensuring one-to-one constraints by removing conflicting mappings.
  • Remove Existing Key Mapping : Deletes the old value from the backward dictionary and the old key from the forward dictionary if the key already exists.
  • Remove Existing Value Mapping : Deletes the old key from the forward dictionary and the old value from the backward dictionary if the value already exists.
  • Add New Mapping : Adds the new key-value pair to both forward and backward dictionaries.
  • Get Item Behavior : Implements the __getitem__ method to retrieve the value associated with a key from the forward dictionary.
  • Backward Lookup : Implements the get_key method to retrieve the key associated with a value from the backward dictionary.
  • Delete Item Behavior : Implements the __delitem__ method to delete a key-value pair by removing it from both forward and backward dictionaries.
  • Key Existence Check : Implements the __contains__ method to check if a key exists in the forward dictionary.
  • Length Calculation : Implements the __len__ method to return the number of key-value pairs in the forward dictionary.
  • String Representation : Implements the __repr__ method to provide a string representation of the BiDict using its forward dictionary.
  • Example Usage : Demonstrates how to use the BiDict class with example data.
  • Set Key-Value Pairs : Adds key-value pairs to the BiDict.
  • Forward Lookup : Retrieves the value associated with a key using the [] operator.
  • Backward Lookup : Retrieves the key associated with a value using the get_key method.
  • Test One-to-One Constraint : Assigns a new key to an existing value to test the one-to-one constraint.
  • Check Key Existence : Verifies if a key still exists after reassigning its value.
  • Confirm New Mapping : Performs a backward lookup to confirm the updated mapping for the reassigned value.

Explanation - Bidirectional Dictionary

  • Concept: Implement a dictionary that maintains one-to-one mappings and allows lookups in both directions.
  • Challenge: Maintain the one-to-one constraint while providing efficient bidirectional access.
  • Key Skills:
    • Data structure design
    • Constraint enforcement
    • Dual-direction lookup implementation
  • Applications:
    • Mapping between IDs and names
    • Translation between different coding systems
    • Maintaining reversible transformations
    • Two-way lookups in caching systems
  • Benefits:
    • Equal efficiency for forward and backward lookups
    • Automatic enforcement of one-to-one relationships
    • Simplified code for bidirectional mapping needs

For more Practice: Solve these Related Problems:

  • Write a Python program to implement a bidirectional dictionary that allows many-to-many mappings.
  • Write a Python function to track reverse lookups in a bidirectional dictionary and prevent duplicate key-value pairs.
  • Write a Python class that extends a bidirectional dictionary to support case-insensitive lookups.
  • Write a Python program to store bidirectional mappings while supporting fast bulk updates.

Python Code Editor:

Previous: Dictionary Merging with Custom Conflict Resolution.
Next: Dictionary-based LRU Cache.

What is the difficulty level of this exercise?

Test your Programming skills with w3resource's quiz.



Follow us on Facebook and Twitter for latest update.