1010
1111import collections
1212
13+ from python_toolbox import comparison_tools
14+
15+
1316KEY , PREV , NEXT = range (3 )
1417
1518
@@ -22,49 +25,53 @@ class OrderedSet(collections.MutableSet):
2225 '''
2326
2427 def __init__ (self , iterable = None ):
25- self .end = end = []
26- end += [None , end , end ] # sentinel node for doubly linked list
27- self .map = {} # key --> [key, prev, next]
28+ self .clear ()
2829 if iterable is not None :
2930 self |= iterable
3031
32+
33+ def clear (self ):
34+ self ._end = []
35+ self ._end += [None , self ._end , self ._end ]
36+ self ._map = {}
37+
3138 def __len__ (self ):
32- return len (self .map )
39+ return len (self ._map )
3340
3441 def __contains__ (self , key ):
35- return key in self .map
42+ return key in self ._map
3643
3744 def add (self , key ):
3845 """
3946 Add an element to a set.
4047
4148 This has no effect if the element is already present.
4249 """
43- if key not in self .map :
44- end = self .end
50+ if key not in self ._map :
51+ end = self ._end
4552 curr = end [PREV ]
46- curr [NEXT ] = end [PREV ] = self .map [key ] = [key , curr , end ]
53+ curr [NEXT ] = end [PREV ] = self ._map [key ] = [key , curr , end ]
4754
4855 def discard (self , key ):
4956 """
5057 Remove an element from a set if it is a member.
5158
5259 If the element is not a member, do nothing.
5360 """
54- if key in self .map :
55- key , prev , next = self .map .pop (key )
61+ if key in self ._map :
62+ key , prev , next = self ._map .pop (key )
5663 prev [NEXT ] = next
5764 next [PREV ] = prev
5865
5966 def __iter__ (self ):
60- end = self .end
67+ end = self ._end
6168 curr = end [NEXT ]
6269 while curr is not end :
6370 yield curr [KEY ]
6471 curr = curr [NEXT ]
6572
6673 def __reversed__ (self ):
67- end = self .end
74+ end = self ._end
6875 curr = end [PREV ]
6976 while curr is not end :
7077 yield curr [KEY ]
@@ -92,3 +99,34 @@ def __del__(self):
9299 self .clear () # remove circular references
93100 # todo: is this really needed? i'm worried about this making the gc not
94101 # drop circulary-referencing objects.
102+
103+ def move_to_end (self , key ):
104+ '''Move an existing element to the end (or beginning if last==False).
105+
106+ Raises KeyError if the element does not exist.
107+ When last=True, acts like a fast version of self[key]=self.pop(key).
108+
109+ '''
110+ # Inefficient implementation until someone cares.
111+ if key in self :
112+ self .remove (key )
113+ self .add (key )
114+
115+
116+ def sort (self , key = None , reverse = False ):
117+ '''
118+ Sort the items according to their keys, changing the order in-place.
119+
120+ The optional `key` argument will be passed to the `sorted` function as
121+ a key function.
122+ '''
123+ # Inefficient implementation until someone cares.
124+ key_function = \
125+ comparison_tools .process_key_function_or_attribute_name (key )
126+ sorted_members = sorted (tuple (self ), key = key_function , reverse = reverse )
127+
128+ self .clear ()
129+ for member in sorted_members [1 :]:
130+ self .move_to_end (member )
131+
132+
0 commit comments