Welcome, guest | Sign In | My Account | Store | Cart
import exceptions
import sys
import types

def copyright():
    print("""Copyright 2001 by Peter Olsen, P.E., p@sigmaxi.org
            May be used for any purpose without payment of royalty
            provided this copyright is retained.
            
            This code is provided in the hope that it may be useful
            but with WITH ABSOLUTELY NO WARRANTY WHATSOEVER,
            including any warranty of merchantability or fitness for any
            particular purpose.

            If this code works, fine; if it doesn't, I'm not responsible.""") 
          
class RaggedArray2D(dictionary):

    """This class implements 2-dimensional ragged arrays as nested Dictionaries.

    Keys are 2-tuples, conceptually "rows" and "columns."

    Each "row" is a subdictionary returned by the first key in a
    keyTuple.  Each "column" is a subdictionary built by extracting
    appropriate elements from each row.  So, for faster code, try to
    access first by row, then by column rather than the other way around

    This class is implemented as two layers of nested dictionaries instead of
    as a single dictionary keyed by the tuples directly because I'm writing it
    for an application in which I want to operate entire rows.  This  approach
    makes it much easier.

    Note: This implementation subclasses a built-in data type, and so requires 
    Python 2.2 or later.  This meets my requirements, but it may not meet
    yours. 
 
    Klaus Alexander Seistrup has described a way to get around this 
    restriction by substituting UserDict.UserDict for dictionary as 
    the base class.  I believe this solves the problem for versions at least
    as far back as 1.5.2.  This approach will require some editing of
    the insertion and retrieval functions to make it work.  

    Thanks Klaus!"""

    def insert(self, keyTuple, newThing):
        """Stores newThing in place specified by keyTuple, returns oldThing.

        keyTuple is a 2-tuple of coordinates.
        thing is any object that can be stored in a dictionary.

        Returns None if there was no old thing at key."""

        if not self.checkKeyTuple(keyTuple):
            raise RaggedArrayException, "Bad keyTuple."
        else:
            # Key was a tuple of length 2
            # So, extract two keys.
            key1 = keyTuple[0]
            key2 = keyTuple[1]

            # So, now we have a sub-dictionary in the right position of
            # raggedArray.  Let's save the oldThing and insert the newThing.
            row = self.getRow(key1)
            oldThing = row.get(key2, None)
            row[key2] = newThing
        # We've saved the oldThing and inserted the newThing.  We're done!
        return oldThing

    def retrieve(self, keyTuple):
        """Retrieves oldThing from place specified by keyTuple.

        keyTuple is a 2-tuple of coordinates."""

        # FIXME: can retrieve() share more code with insert()?
        if not self.checkKeyTuple(keyTuple):
            raise RaggedArrayException, "Bad keyTuple."
        else:
            # Key was a tuple of length 2
            # So, extract two keys.
            key1 = keyTuple[0]
            key2 = keyTuple[1]

            # So, now we have a sub-dictionary in the right position of
            # raggedArray.
            row = self.getRow(key1)
            oldThing = row.get(key2, None)
        return oldThing

    def checkKeyTuple(self, keyTuple):
        """Checks keyTuple validity. Returns boolean."""
        
        # Is the key a tuple?  If not, it's an error.
        if not isinstance(keyTuple, types.TupleType):
            result = 0
            raise RaggedArrayException, "checkKeyTuple: keyTuple not a tuple."
        # Does the keyTuple have two keys?  If not, it's an error.
        elif len(keyTuple) != 2:
            result = 0
            raise RaggedArrayException, "checkKeyTuple: keyTuple wrong length."
        else:
            result = 1
        return result

    def getRow(self, key):
        """Gets or creates the row subdirectory matching key.

        Note that the "rows" returned by this method need not be complete.
        They will contain only the items corresponding to the keys that
        have actually been inserted.  Other items will not be present,
        in particular they will not be represented by None."""
        
        # Get whatever is located at the first key
        thing = self.get(key, None)

        # If thing is a dictionary, return it.
        if isinstance(thing, dictionary):
            row = thing

        # If thing is None, create a new dictionary and return that.
        elif isinstance(thing, types.NoneType):
            row = {}
            self[key] = row

        # If thing is neither a dictionary nor None, then it's an error.
        else:
            row = None
            raise RaggedArrayException, "getRow: thing was not a dictionary."
        return row  

    def getColumn(self, columnKey):
        """Gets or creates the column subdirectory matching key.

        Note that the "columns" returned by this method need not be complete.
        They will contain only the items corresponding to the keys that
        have actually been inserted.  Other items will not be present,
        in particular they will not be represented by None."""
        
        # Get a list of the keys to the primary dictionary
        rows = self.keys()

        # Create the empty column dictionary
        column = {}

        # Now index through the rows, retrieving the appropriate "column"
        # entries and adding them to the column
        for row in rows:
            thisRow = self.getRow(row)
            if thisRow.has_key(columnKey):
                column[row] = thisRow[columnKey]
        return column
    
class RaggedArrayException(exceptions.Exception):
    def __init__(self, args=None):
        self.args = "RaggedArray: " + args
        

       

History

  • revision 2 (22 years ago)
  • previous revisions are not available