ASPN ActiveState Programmer Network  
ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

Title: Ordered Dictionary
Submitter: David Benjamin (other recipes)
Last Updated: 2002/01/21
Version no: 1.2
Category: Extending

 

4 stars 5 vote(s)


Description:

This dictionary class extends UserDict to record the order in which items are added. Calling keys(), values(), items(), etc. will return results in this order. This works similarly to the array type in PHP.

Source: Text Source

from UserDict import UserDict

class odict(UserDict):
    def __init__(self, dict = None):
        self._keys = []
        UserDict.__init__(self, dict)

    def __delitem__(self, key):
        UserDict.__delitem__(self, key)
        self._keys.remove(key)

    def __setitem__(self, key, item):
        UserDict.__setitem__(self, key, item)
        if key not in self._keys: self._keys.append(key)

    def clear(self):
        UserDict.clear(self)
        self._keys = []

    def copy(self):
        dict = UserDict.copy(self)
        dict._keys = self._keys[:]
        return dict

    def items(self):
        return zip(self._keys, self.values())

    def keys(self):
        return self._keys

    def popitem(self):
        try:
            key = self._keys[-1]
        except IndexError:
            raise KeyError('dictionary is empty')

        val = self[key]
        del self[key]

        return (key, val)

    def setdefault(self, key, failobj = None):
        UserDict.setdefault(self, key, failobj)
        if key not in self._keys: self._keys.append(key)

    def update(self, dict):
        UserDict.update(self, dict)
        for key in dict.keys():
            if key not in self._keys: self._keys.append(key)

    def values(self):
        return map(self.get, self._keys)

Discussion:

Often it is useful to use a dictionary to store information where the order of that information matters. In Python, one must usually keep a list of keys and pass the list along with the dictionary to any functions that need to maintain this order. This implementation stores the list of keys internally and overrides the usual accessor methods to keep the list up to date with each operation.

Example:

>>> d = odict()
>>> d['a'] = 1
>>> d['b'] = 2
>>> d['c'] = 3
>>> d.items()
[('a', 1), ('b', 2), ('c', 3)]

popitem() has been fixed in this version to throw the correct exception on an empty dictionary.

Note that all operations on the key list are O(n).



Add comment

Number of comments: 8

Bug in __init__ regarding _keys, Hamish Lawson, 2002/01/18
If UserDict.__init__ is actually passed a dictionary from __init__, then it will attempt to call update; but _keys isn't yet defined, and so an error will be raised. Therefore _keys must be defined first:

    def __init__(self, dict = None):
        self._keys = []
        UserDict.__init__(self, dict)

Add comment

Bug in __init__ regarding _keys, David Benjamin, 2002/01/21
I made the above change to the source. Thanks for catching this.
Add comment

Ordered Dictionary at Parnassus, Wolfgang Grafen, 2002/02/13
There is a more complete module addressing this topic under http://home.arcor.de/wolfgang.grafen/Python/Modules/Modules.html called seqdict.
* seqdict keeps one value for one key
* mseqdict keeps multiple values per key and is the closest emulation of areal world dictionary.
(Search Parnassus for seqdict)
Add comment

Serious bug in copy(), Jon Nelson, 2002/12/19
There is a serious problem with copy() The returned object instance is one of UserDict not of odict. I use:

def copy(self):
  newInstance = odict()
  newInstance.update(self)
  return newInstance
and for update:
def update(self, dict):
  for (key,val) in dict.items():
    self.__setitem__(key,val)

Add comment

keys() should return a copy of self._keys, José Esteves, 2003/01/01
It would be better to have

  def keys (self):
    return self._keys[:]
to prevent changes to self._keys by callers of the keys() method.
Add comment

Bug in setdefault, Brian Hawthorne, 2004/12/02
setdefault returns the value (new or existing) for the given key.
here is a correct setdefault:

def setdefault(self, key, failobj = None):
    if key not in self._keys: self._keys.append(key)
    return UserDict.setdefault(self, key, failobj)

Add comment

Extending to Sorted Dictionary, Charlie Taylor, 2006/08/06
I needed a Sorted Dictionary and this Ordered Dictionary made a great starting point. I substituted: bisect.insort(self._keys,key) for: self._keys.append(key) in a few places and I was there. Thanks for the help
Add comment

variations in base class, ruedi w, 2008/02/07
you may want to derive from IterableUserDict to allow for things like [k for k in mydict].
or in python >=2.2: derive directly from dict.
if you cant decide, build a mixin using super() or a wrapper - this gives the ability to order any dict-like object
Add comment



Highest rated recipes:

1. A simple XML-RPC server

2. Web service accessible ...

3. IPy Notify

4. Treat the Win32 Registry ...

5. a friendly mkdir()

6. Wrapping template engine ...

7. Assignment in expression

8. Changing return value ...

9. Implementation of sets ...

10. bag collection class




Privacy Policy | Email Opt-out | Feedback | Syndication
© 2006 ActiveState Software Inc. All rights reserved.