Skip to content
Browse files

Merge pull request #212 from lukecampbell/orderedset

Adds OrderedSet to pyon utils
  • Loading branch information...
2 parents 5841d02 + 2cac450 commit e0e3ab514c4335d88cab7fd47d2ac65ce8892d87 @daf daf committed Jan 2, 2013
Showing with 133 additions and 0 deletions.
  1. +71 −0 pyon/util/set.py
  2. +62 −0 pyon/util/test/test_set.py
View
71 pyon/util/set.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+'''
+@author (http://code.activestate.com/recipes/576694/)
+@file pyon/util/set.py
+@date Wed Jan 2 10:43:06 EST 2013
+'''
+## {{{ http://code.activestate.com/recipes/576694/ (r9)
+import collections
+
+class OrderedSet(collections.MutableSet):
+
+ def __init__(self, iterable=None):
+ self.end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.map = {} # key --> [key, prev, next]
+ if iterable is not None:
+ self |= iterable
+
+ def __len__(self):
+ return len(self.map)
+
+ def __contains__(self, key):
+ return key in self.map
+
+ def add(self, key):
+ if key not in self.map:
+ end = self.end
+ curr = end[1]
+ curr[2] = end[1] = self.map[key] = [key, curr, end]
+
+ def discard(self, key):
+ if key in self.map:
+ key, prev, next = self.map.pop(key)
+ prev[2] = next
+ next[1] = prev
+
+ def __iter__(self):
+ end = self.end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ def pop(self, last=True):
+ if not self:
+ raise KeyError('set is empty')
+ key = self.end[1][0] if last else self.end[2][0]
+ self.discard(key)
+ return key
+
+ def __repr__(self): #pragma no cover
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, list(self))
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedSet):
+ return len(self) == len(other) and list(self) == list(other)
+ return set(self) == set(other)
+
+
+## end of http://code.activestate.com/recipes/576694/ }}}
+
View
62 pyon/util/test/test_set.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env/python
+
+'''
+@author Luke Campbell <LCampbell (at) ASAScience.com>
+@file pyon/util/test/test_set.py
+@date Wed Jan 2 10:46:48 EST 2013
+'''
+
+from pyon.util.set import OrderedSet
+from pyon.util.unit_test import PyonTestCase
+from nose.plugins.attrib import attr
+from copy import copy
+
+@attr('UNIT')
+class OrderedSetTest(PyonTestCase):
+ def test_set_ops(self):
+ # Verify unique keys and order is maintained
+ os = OrderedSet([3,1,5,2,2,1,9,8,12,18])
+ compare_list = [3,1,5,2,9,8,12,18]
+ self.assertEquals(list(os), compare_list)
+
+
+ # Verify addition doesn't violate unique keys
+ os.add(2)
+ self.assertEquals(list(os), compare_list)
+
+ # Verify a valid addition doesn't violate order
+ os.add(21)
+ self.assertEquals(list(os), compare_list + [21])
+
+ # Pop
+ val = os.pop()
+ self.assertEquals(val, 21)
+ self.assertEquals(list(os), compare_list)
+
+ # Verify list comprehension doesn't violate order
+ self.assertEquals([i for i in os], compare_list)
+
+ # Lenght assertions
+ self.assertEquals(len(list(os)), len(os))
+
+ # Reversed
+ cmpval = copy(compare_list)
+ cmpval.reverse()
+ self.assertEquals([i for i in reversed(os)], cmpval)
+
+
+ # Comparisons
+ self.assertTrue(os == compare_list)
+ os2 = OrderedSet(compare_list)
+ self.assertTrue(os == os2)
+
+
+ # Discarding
+ self.assertTrue(8 in os)
+ os.discard(8)
+ self.assertFalse(8 in os)
+
+
+ with self.assertRaises(KeyError):
+ os2 = OrderedSet([])
+ os2.pop()

0 comments on commit e0e3ab5

Please sign in to comment.
Something went wrong with that request. Please try again.