I have an OrderedDict, I need to add an element while maintaining sorting
import sys
import bisect
from collections import OrderedDict
arr = {('a',1111),('b',2222),('f',3333)}
arr = OrderedDict(arr)
bisect.insort(arr,('c',4444))
#expectedly arr = {('a',1111),('b',2222),('c',4444),('f',3333)}
#but actually TypeError: collections.OrderedDict is not a sequence
Update: I need items to be stored sorted by key but with
import sys
import bisect
from collections import OrderedDict
from sortedcontainers import sorteddict
arr = {('a',1111),('b',2222),('f',3333)}
arr = OrderedDict(arr)
arr.update({'c':4444}) #or arr['c'] = 4444
print(arr)
OrderedDict([('b', 2222), ('f', 3333), ('a', 1111), ('c', 4444)])
instead OrderedDictх([('a',1111),('b',2222),('c',4444),('f',3333)])
like map in c ++
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:
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__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.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:
class SortOrderedDict(dict):
__setitem__
method. The following uses bisect
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)
update
method - this class is a subclass of dict
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
update = __update = _collections_abc.MutableMapping.update
to __update = update
__reduce__
method change the class name in for k in vars(OrderedDict()):
to whatever you you named your class for k in vars(SortOrderedDict()):
__eq__
method. Change if 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
>>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With