Slicing a Python OrderedDict
Solution 1
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):
from odict import OrderedDict as odict
p = odict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print p[1:3]
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)):
from ruamel.ordereddict import ordereddict
q = ordereddict(o, relax=True)
print q[1:3]
r = odict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print r[1:3]
If you want (or have to) stay within the standard library you can sublass collections.OrderedDict
's __getitem__
:
class SlicableOrderedDict(OrderedDict):
def __getitem__(self, k):
if not isinstance(k, slice):
return OrderedDict.__getitem__(self, k)
x = SlicableOrderedDict()
for idx, key in enumerate(self.keys()):
if k.start <= idx < k.stop:
x[key] = self[key]
return x
s = SlicableOrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print s[1:3]
of course you could use Martijn's or Jimmy's shorter versions to get the actual slice that needs returning:
from itertools import islice
class SlicableOrderedDict(OrderedDict):
def __getitem__(self, k):
if not isinstance(k, slice):
return OrderedDict.__getitem__(self, k)
return SlicableOrderedDict(islice(self.viewitems(), k.start, k.stop))
t = SlicableOrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print t[1:3]
or if you just want smarten up all existing OrderedDict
s without subclassing:
def get_item(self, k):
if not isinstance(k, slice):
return OrderedDict._old__getitem__(self, k)
return OrderedDict(islice(self.viewitems(), k.start, k.stop))
OrderedDict._old__getitem__ = OrderedDict.__getitem__
OrderedDict.__getitem__ = get_item
u = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
print u[1:3]
Solution 2
You can use the itertools.islice
function, which takes an iterable and outputs the stop
first elements. This is beneficial since iterables don't support the common slicing method, and you won't need to create the whole items
list from the OrderedDict.
from collections import OrderedDict
from itertools import islice
o = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
sliced = islice(o.iteritems(), 3) # o.iteritems() is o.items() in Python 3
sliced_o = OrderedDict(sliced)
Solution 3
In Python 2, you can slice the keys:
x.keys()[1:3]
and to support both Python 2 and Python 3, you'd convert to a list first:
list(k)[1:3]
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:
from itertools import islice
list(islice(x, 1, 3))
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 3 dict.items()
provides. You can pass the islice()
object straight to another OrderedDict()
in this case:
OrderedDict(islice(x.items(), 1, 3)) # x.viewitems() in Python 2
Solution 4
I was able to slice an OrderedDict using the following:
list(myordereddict.values())[start:stop]
I didn't test the performance.
Related videos on Youtube
Alice
Updated on September 15, 2022Comments
-
Alice over 1 year
In my code I frequently need to take a subset range of keys+values from a Python
OrderedDict
(fromcollections
package). Slicing doesn't work (throwsTypeError: 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?
-
Andy Hayden over 7 yearsIs this O(n)? it seems like this operation needn't be.
-
Eric Lindauer over 4 yearsThis is helpful, thanks! Note that the SlicableOrderedDict does not support negative indices as written.
-
amball over 3 yearsIn Python 3.7, I get the error
collections.OrderedDict' object has no attribute 'iteritems
-
lutuh over 3 yearsUse
o.items()
in Python 3. -
diman82 over 2 yearsI get an error: 'TypeError: unhashable type: 'slice' when using odict package
-
diman82 over 2 yearsAlso, changing the built-in getitem implementation (without subclassing) yields in 'TypeError: can't set attributes of built-in/extension type 'collections.OrderedDict' (for subclassing example change self.viewitems() to self.items() for python 3.x)
-
IgorZ over 2 yearsThis does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review
-
Jeremy Caney over 2 years@SureshKumarVeluswamy: Why not? It's short. It should include more explanation. It might even be redundant with one of the other six answers. But certainly it's an answer.