In my code I frequently need to take a subset range of keys+values from a Python OrderedDict
(from collections
package). Slicing doesn't work (throws TypeError: unhashable type
) and the alternative, iterating, is cumbersome:
from collections import OrderedDict
o = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
# want to do:
# x = o[1:3]
# need to do:
x = OrderedDict()
for idx, key in enumerate(o):
if 1 <= idx < 3:
x[key] = o[key]
Is there a better way to get this done?
The ordered dict in the standard library, doesn't provide that functionality. Even though libraries existed for a few years before collections.OrderedDict that have this functionality (and provide essentially a superset of OrderedDict): voidspace odict and ruamel.ordereddict (I am the author of the latter package, which is a reimplementation of odict in C):
In ruamel.ordereddict you can relax the ordered input requirement (AFAIK you cannot ask derivative of dict if its keys are ordered (would be good addition to ruamel.ordereddict to recognise collection.OrderedDicts)):
If you want (or have to) stay within the standard library you can sublass
collections.OrderedDict
's__getitem__
:of course you could use Martijn's or Jimmy's shorter versions to get the actual slice that needs returning:
or if you just want smarten up all existing
OrderedDict
s without subclassing:I wanted to slice using a key, since I didn't know the index in advance:
or to slice from
start
tostop
:This allows for:
In Python 2, you can slice the keys:
and to support both Python 2 and Python 3, you'd convert to a list first:
The Python 2
OrderedDict.keys()
implementation does exactly that.In both cases you are given a list of keys in correct order. If creating a whole list first is an issue, you can use
itertools.islice()
and convert the iterable it produces to a list:All of the above also can be applied to the items; use
dict.viewitems()
in Python 2 to get the same iteration behaviour as Python 3dict.items()
provides. You can pass theislice()
object straight to anotherOrderedDict()
in this case:You can use the
itertools.islice
function, which takes an iterable and outputs thestop
first elements. This is beneficial since iterables don't support the common slicing method, and you won't need to create the wholeitems
list from the OrderedDict.