Add method to OrderedDict for repositioning keys to the ends.

This commit is contained in:
Raymond Hettinger 2010-09-06 21:26:09 +00:00
parent 7b2a7710ef
commit f45abc97bf
5 changed files with 51 additions and 8 deletions

View File

@ -793,6 +793,23 @@ the items are returned in the order their keys were first added.
(key, value) pair. The pairs are returned in LIFO order if *last* is true
or FIFO order if false.
.. method:: move_to_end(key, last=True)
Move an existing *key* to either end of an ordered dictionary. The item
is moved to the right end if *last* is true (the default) or to the
beginning if *last* is false. Raises :exc:`KeyError` if the *key* does
not exist::
>>> d = OrderedDict.fromkeys('abcde')
>>> d.move_to_end('b')
>>> ''.join(d.keys)
'acdeb'
>>> d.move_to_end('b', 0)
>>> ''.join(d.keys)
'bacde'
.. versionadded:: 3.2
In addition to the usual mapping methods, ordered dictionaries also support
reverse iteration using :func:`reversed`.

View File

@ -173,18 +173,29 @@ class OrderedDict(dict, MutableMapping):
def __del__(self):
self.clear() # eliminate cyclical references
def _renew(self, key, PREV=0, NEXT=1):
'Fast version of self[key]=self.pop(key). Private method for internal use.'
def move_to_end(self, key, last=True, PREV=0, NEXT=1):
'''Move an existing element to the end (or beginning if last==False).
Raises KeyError if the element does not exist.
When last=True, acts like a fast version of self[key]=self.pop(key).
'''
link = self.__map[key]
link_prev = link[PREV]
link_next = link[NEXT]
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
root = self.__root
last = root[PREV]
link[PREV] = last
link[NEXT] = root
last[NEXT] = root[PREV] = link
if last:
last = root[PREV]
link[PREV] = last
link[NEXT] = root
last[NEXT] = root[PREV] = link
else:
first = root[NEXT]
link[PREV] = root
link[NEXT] = first
root[NEXT] = first[PREV] = link
################################################################################

View File

@ -127,7 +127,7 @@ def lru_cache(maxsize=100):
len=len, KeyError=KeyError):
cache = OrderedDict() # ordered least recent to most recent
cache_popitem = cache.popitem
cache_renew = cache._renew
cache_renew = cache.move_to_end
kwd_mark = object() # separate positional and keyword args
lock = Lock()

View File

@ -973,7 +973,19 @@ class TestOrderedDict(unittest.TestCase):
od['a'] = 1
self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
def test_move_to_end(self):
od = OrderedDict.fromkeys('abcde')
self.assertEqual(list(od), list('abcde'))
od.move_to_end('c')
self.assertEqual(list(od), list('abdec'))
od.move_to_end('c', 0)
self.assertEqual(list(od), list('cabde'))
od.move_to_end('c', 0)
self.assertEqual(list(od), list('cabde'))
od.move_to_end('e')
self.assertEqual(list(od), list('cabde'))
with self.assertRaises(KeyError):
od.move_to_end('x')
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
type2test = OrderedDict

View File

@ -13,6 +13,9 @@ Core and Builtins
Library
-------
- collections.OrderedDict now supports a new method for repositioning
keys to either end.
- Issue #9754: Similarly to assertRaises and assertRaisesRegexp, unittest
test cases now also have assertWarns and assertWarnsRegexp methods to
check that a given warning type was triggered by the code under test.