Data Structures and Algorithms using Python
Copyright © 2023 by Rance Necaise
Table Of Contents
Data Structures and Algorithms using Python
Copyright © 2023
by Rance Necaise

2.9 Map: Single-List Implementation

Instead of using two lists to store the key/value entries in the map, we can use a single list. The individual keys and corresponding values can both be saved in a storage object that is then added to the list. A sample instance illustrating the data organization required for this approach is shown in Figure 2.9.1.

Figure 2.9.1: The Map ADT implemented using a single list.

Storage Class

As we indicated earlier in Chapter 1, we want to avoid the use of tuples when storing structured data since it is better practice to use classes with named fields. The MapEntry storage class will be used to store the individual key/value pairs:

class MapEntry :                        
  def __init__(self, key, value) :
    self.key = key
    self.value = value        

Note this storage class is intended to be private since it is only intended for use by the Map class that provides the single list implementation of the Map ADT.

Constructor and Length Operations

The implementation of the constructor only involves the creation and storage of an empty list to hold the map entries:

class Map :
  def __init__(self) :
    self._entryList = list()

As with the Set class implemented earlier in the chapter, the length operation can simply return the length of the list containing the storage objects:

def __len__(self) :
  return len(self._entryList)  

Adding Entries

Many of the methods require a search to determine if the map contains a given key. In this implementation, the standard in operator cannot be used since the list contains MapEntry objects and not simply key entries. Instead, we have to search the list ourselves and examine the key field of each MapEntry object. Likewise, we routinely have to locate within the list the position containing a specific key/value entry. Since these operations will be needed in several methods, we can create a helper method that combines the two searches and use it where needed.

def _findPosition(self, key) :
  for i in range(len(self)) :
    if self._entryList[i].key == key :
      return i        
  return -1

The _findPosition helper method searches the list for the given key. If the key is found, the index of its location is returned; otherwise, the function returns -1 to indicate the key is not contained in the map.

When used by the other methods, the value returned by the helper method can be evaluated to determine both the existence of the key and the location of the corresponding entry if the key is in the map. Given the helper method, we can now implement the add operation:

def add(self, key, value) :
  ndx = self._findPosition(key)
  if ndx >= 0 :
    self._entryList[ndx].value = value
    return False
  else :  
    entry = MapEntry(key, value)
    self._entryList.append(entry)
    return True  

By combining the two searches into a single operation, we eliminate the need to first determine if the map contains the key and then having to search again for its location.

Source Listing

The implementation of the Map ADT using a single list to store the entries is provided in the listing below. The implementation of the remove, clear, and valueOf methods are left as an exercise. The implementation of the map iterator is also left as an exercise. As with the search operation, we can not simply iterate over the elements of the list contained in the _entryList instance variable since the elements contain storage objects comprised of both a key and value.

Program Listing
Program: linearmap.py
  1. # Implementation of Map ADT using a single list.
  2. class Map :
  3.    # Creates an empty map instance.
  4.   def __init__(self) :
  5.     self._entryList = list()
  6.      
  7.    # Returns the number of entries in the map.
  8.   def __len__(self) :
  9.     return len(self._entryList)  
  10.  
  11.    # Determines if the map contains the given key.
  12.   def __contains__(self, key) :
  13.     ndx = self._findPosition(key)
  14.     return ndx is not None
  15.    
  16.    # Adds a new entry to the map if the key does exist. Otherwise, the
  17.    # new value replaces the current value associated with the key.
  18.   def add(self, key, value) :
  19.     ndx = self._findPosition(key)
  20.     if ndx >= 0 :  # if the key was found
  21.       self._entryList[ndx].value = value
  22.       return False
  23.     else :  # otherwise add a new entry
  24.       entry = MapEntry(key, value)
  25.       self._entryList.append(entry)
  26.       return True  
  27.      
  28.    # --- The remaining methods go here. ---
  29.    
  30.    # Returns an iterator for traversing the keys in the map.
  31.   def __iter__(self) :
  32.     return MapIterator(self._entryList)
  33.    
  34.    # Helper method used to find the index position of a key. If the
  35.    # key is not found, None is returned.
  36.   def _findPosition(self, key) :
  37.      # Iterate through each entry in the list.
  38.     for i in range(len(self)) :
  39.        # Is the key stored in the ith entry?
  40.       if self._entryList[i].key == key :
  41.         return i        
  42.      # When not found, return None.
  43.     return -1
  44.    
  45. # Private storage class for holding the key/value pairs.  
  46. class MapEntry :                        
  47.   def __init__(self, key, value) :
  48.     self.key = key
  49.     self.value = value        
Page last modified on August 19, 2023, at 07:25 PM