如何获取有序字典中的“下一个”项目?

13

我正在使用一个OrderedDict来随机访问一个列表,但现在想要获取与我当前拥有的项相邻的下一项

foo = OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
apple = foo['apple']

我如何仅使用fooapple获取香蕉?


1
OrderedDict 对于这个似乎太简单了。也许可怕的一行代码会起作用? foo[(lambda keys: keys[(keys.index('pear') + 1) % len(keys)])(foo.keys())] - Blender
4个回答

9
如果您可以访问有意保留为私有的OrderedDict实现的那些部分,那么就没问题了。
>>> class MyOrderedDict(OrderedDict):
...     def next_key(self, key):
...             next = self._OrderedDict__map[key][1]
...             if next is self._OrderedDict__root:
...                     raise ValueError("{!r} is the last key".format(key))
...             return next[2]
...     def first_key(self):
...             for key in self: return key
...             raise ValueError("OrderedDict() is empty")
... 
>>> od = MyOrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
>>> od.next_key("apple")
'banana'
>>> od.next_key("banana")
'orange'
>>> od.next_key("orange")
'pear'
>>> od.next_key("pear")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in next_key
ValueError: 'pear' is the last key
>>> od.first_key()
'apple'

1
超出了我的期望,这正是我所希望得到的洞察力。 - John Mee
在Python 3.4中,OrderedDict的实现略有改变(请参见此处)。链接列表上的元素现在是一个名为 _Link 的虚拟类的对象。不再使用 self._OrderedDict__map[key][1] 访问,而应该使用 self._OrderedDict__map[key].next - Andrés
在Python3.5中,似乎有更多的变化。OrderedDict的实现已经从Python移动到了C。请查看[https://bugs.python.org/issue16991]。这个实现将不允许继承_map即_OrderedDict__map。如果你必须规避这个问题并使用Python模块,可以尝试使用 `py_coll = import_fresh_module('collections', blocked=['_collections'])OrderedDict = py_coll.OrderedDict` - Mikki
13
为什么OrderedDict的接口中没有实现这个功能呢?它似乎是OrderedDict一个非常显然的使用场景,令人惊讶的是它们没有比在代码中迭代整个OrderedDict更简单的导航方式。想知道背后的原因是什么。 - Anthony Manning-Franklin

6

我不敢想象在一个很大的列表中这样做会有多慢,但这是我目前想到的唯一方法...

>>> foo.items()[foo.keys().index('apple') + 1]
('banana', 3)

编辑:

这个例子有些牵强;我实际的集合是按日期键入的。如果我需要在今天之后获取条目,则可以使用dropwhile解决方法...

>>> foo = OrderedDict([(datetime.date(2000,1,1), 4), (datetime.date(2000,5,23), 3), datetime.date(2000,10,1), 2), (datetime.date(2000,12,31), 1)])
>>> today = datetime.date(2000,1,30)
>>> foo.items()[foo.keys().index((itertools.dropwhile(lambda d: d<today, foo)).next())]
(datetime.date(2000, 5, 23), 3)

相当冗长。


3

Python 3.X

dict.items 会返回一个可迭代的字典视图对象,而不是列表。为了使索引操作成为可能,我们需要将其包装在一个列表中:

>>> from collections import OrderedDict
>>> 
>>> foo = OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])
>>> 
>>> def next_item(odic, key):
...     return list(odic)[list(odic.keys()).index(key) + 1]
... 
>>> next = next_item(foo, 'apple')
>>> print(next, foo[next])
banana 3

1

从您的代码进行了修改,我认为这种方式会更好一些:

import collections as co
import datetime as dt
import itertools as it

foo = co.OrderedDict([
    (dt.date(2000,1,1), 4),
    (dt.date(2000,5,23), 3),
    (dt.date(2000,10,1), 2),
    (dt.date(2000,12,31), 1)
])
today = dt.date(2000,1,30)

fooiter = it.dropwhile(lambda d: d <= today, foo)
print next(fooiter)
print list(fooiter)

基本上,在正确的位置拥有迭代器已经足够了。

从任何位置开始迭代会很酷,但不确定是否可行。需要一些思考。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接