Add the new item to the original items, sort, make a new dict:
>>> arr = {['a',1111],['b',2222],['f',3333]}
>>> arr = collections.OrderedDict[arr]
>>> new = ['c',4444]
>>> items = list[arr.items[]]
>>> items.append[new]
>>> items.sort[]
>>> arr = collections.OrderedDict[items]
>>> arr
OrderedDict[[['a', 1111], ['b', 2222], ['c', 4444], ['f', 3333]]]
Or a bit more involved option:
- Subclass collections.OrderedDict
- Using the
move_to_end
method as a guide create a new method that will traverse the doubly linked list; find the place to insert; then insert the new key - maybe bisect can be used here or some other doubly linked list sorted insertion algorithm - override the
__setitem__
method and within it call the new method - or just replace the add-new-key-to-the-end code with the algorithm you came up with in the previous bullet.
Sorted dict that maintains key sorted order
I couldn't figure out how to make an OrderedDict subclass work - it has a number of attributes that get name mangled - only one or two methods need to be overridden and I didn't want to spend the time figuring out the name-mangling aspect.
So just copy the whole OrderedDict class from the source from here - to here into a separate module, so you can import it, and include these imports.
from _weakref import proxy as _proxy
from collections import _Link, _OrderedDictKeysView
from collections import _OrderedDictItemsView, _OrderedDictValuesView
import _collections_abc
from _weakref import proxy as _proxy
from reprlib import recursive_repr as _recursive_repr
from operator import itemgetter as _itemgetter, eq as _eq
import bisect
Then change the following in the class:
- The class name of course to whatever you like. Following the class name is a docstring and some comments describing the class's behaviour - these should be updated.
class SortOrderedDict[dict]:
- Override the
__setitem__
method. The following usesbisect
to find the insertion order. Don't know if it is really warranted, it has to make a list of a dict keys view first but that part should be fast C code [? guessing here]
def __setitem__[self, key, value,
dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link]:
'od.__setitem__[i, y] od[i]=y'
# Setting a new item creates a new link in the linked list,
# inserted at its key sorted position - uses less than comparisons,
# and the inherited dictionary is updated with the new key/value pair.
if key not in self:
self.__map[key] = link = Link[]
root = self.__root
last = root.prev
link.key = key
curr = root.next
if curr is root: # first item!
link.prev, link.next = last, root
last.next = link
root.prev = proxy[link]
elif link.key < root.next.key: # at the beginning?
#print[f'{link.key} before {root.next.key}']
soft_link = root.next
link.prev, link.next = root, soft_link
soft_link.prev = link
root.next = link
elif root.prev.key < link.key: # at the end?
#print[f'{link.key} at the end after {root.prev.key}']
soft_link = root.prev
link.prev, link.next = soft_link, root
soft_link.next = link
root.prev = proxy[link]
else: # in the middle somewhere - use bisect
keys = list[self.keys[]]
i = bisect.bisect_left[keys,key]
right = self.__map[keys[i]]
#print[f'{link.key} between {right.prev.key} and {right.key}']
soft_link = right.prev
link.prev,link.next = soft_link,right
right.prev = link
soft_link.next = link
dict_setitem[self, key, value]
- Add an
update
method - this class is a subclass ofdict
this overrides its update method forcing it to use__setitem__
.
def update[self,other]:
try:
other = other.items[]
except AttributeError:
pass
for k,v in other:
self[k] = v
- Change this line
update = __update = _collections_abc.MutableMapping.update
to
__update = update
- In the
__reduce__
method change the class name infor k in vars[OrderedDict[]]:
to whatever you you named your class
for k in vars[SortOrderedDict[]]:
- Same thing in the
__eq__
method. Changeif isinstance[other, OrderedDict]:
to
if isinstance[other, SortOrderedDict]:
If using bisect doesn't seem worthwhile just traverse the linked list till the insertion point is found. [All the other changes listed above still apply]
def __setitem__[self, key, value,
dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link]:
'od.__setitem__[i, y] od[i]=y'
# Setting a new item creates a new link in the linked list,
# inserted at its key sorted position - uses less than comparisons,
# and the inherited dictionary is updated with the new key/value pair.
if key not in self:
self.__map[key] = link = Link[]
root = self.__root
last = root.prev
link.key = key
curr = root.next
if curr is root: # first item!
link.prev, link.next = last, root
last.next = link
root.prev = proxy[link]
# traverse the linked list; find sorted insertion point; insert
while curr is not root:
if link.key < curr.key:
soft_link = curr.prev
soft_link.next = link
link.prev = soft_link
link.next = curr
curr.prev = link
break
elif curr.next is root:
link.prev, link.next = curr, root
curr.next = link
root.prev = proxy[link]
break
curr = curr.next
dict_setitem[self, key, value]
Usage
>>> arr = {['a',1111],['f',3333],['b',2222]}
>>> arr = SortOrderedDict[arr]
>>> arr
SortOrderedDict[[['a', 1111], ['b', 2222], ['f', 3333]]]
>>> other = {k:v for k,v in zip['tvsnpqkl',range[8]]}
>>> arr.update[other]
>>> arr
SortOrderedDict[[['a', 1111], ['b', 2222], ['f', 3333], ['k', 6], ['l', 7], ['n', 3], ['p', 4], ['q', 5], ['s', 2], ['t', 0], ['v', 1]]]
>>> b = SortOrderedDict[[['a',1111],['f',3333],['b',2222]]]
>>> b.update[other]
>>> arr == b
True
>>> b == arr
True
>>>