Accessing items in an collections.OrderedDict by index
Solution 1:
If its an OrderedDict()
you can easily access the elements by indexing by getting the tuples of (key,value) pairs as follows
>>> import collections
>>> d = collections.OrderedDict()
>>> d['foo'] = 'python'
>>> d['bar'] = 'spam'
>>> d.items()
[('foo', 'python'), ('bar', 'spam')]
>>> d.items()[0]
('foo', 'python')
>>> d.items()[1]
('bar', 'spam')
Note for Python 3.X
dict.items
would return an iterable dict view object rather than a list. We need to wrap the call onto a list in order to make the indexing possible
>>> items = list(d.items())
>>> items
[('foo', 'python'), ('bar', 'spam')]
>>> items[0]
('foo', 'python')
>>> items[1]
('bar', 'spam')
Solution 2:
Do you have to use an OrderedDict or do you specifically want a map-like type that's ordered in some way with fast positional indexing? If the latter, then consider one of Python's many sorted dict types (which orders key-value pairs based on key sort order). Some implementations also support fast indexing. For example, the sortedcontainers project has a SortedDict type for just this purpose.
>>> from sortedcontainers import SortedDict
>>> sd = SortedDict()
>>> sd['foo'] = 'python'
>>> sd['bar'] = 'spam'
>>> print sd.iloc[0] # Note that 'bar' comes before 'foo' in sort order.
'bar'
>>> # If you want the value, then simple do a key lookup:
>>> print sd[sd.iloc[1]]
'python'
Solution 3:
Here is a special case if you want the first entry (or close to it) in an OrderedDict, without creating a list. (This has been updated to Python 3):
>>> from collections import OrderedDict
>>>
>>> d = OrderedDict()
>>> d["foo"] = "one"
>>> d["bar"] = "two"
>>> d["baz"] = "three"
>>> next(iter(d.items()))
('foo', 'one')
>>> next(iter(d.values()))
'one'
(The first time you say "next()", it really means "first.")
In my informal test, next(iter(d.items()))
with a small OrderedDict is only a tiny bit faster than items()[0]
. With an OrderedDict of 10,000 entries, next(iter(d.items()))
was about 200 times faster than items()[0]
.
BUT if you save the items() list once and then use the list a lot, that could be faster. Or if you repeatedly { create an items() iterator and step through it to to the position you want }, that could be slower.
Solution 4:
It is dramatically more efficient to use IndexedOrderedDict from the indexed
package.
Following Niklas's comment, I have done a benchmark on OrderedDict and IndexedOrderedDict with 1000 entries.
In [1]: from numpy import *
In [2]: from indexed import IndexedOrderedDict
In [3]: id=IndexedOrderedDict(zip(arange(1000),random.random(1000)))
In [4]: timeit id.keys()[56]
1000000 loops, best of 3: 969 ns per loop
In [8]: from collections import OrderedDict
In [9]: od=OrderedDict(zip(arange(1000),random.random(1000)))
In [10]: timeit od.keys()[56]
10000 loops, best of 3: 104 µs per loop
IndexedOrderedDict is ~100 times faster in indexing elements at specific position in this specific case.
Other solutions listed require an extra step. IndexedOrderedDict
is a drop-in replacement for OrderedDict
, except it's indexable.