Shortest way to get first item of `OrderedDict` in Python 3

ぐ巨炮叔叔 提交于 2019-11-30 04:15:20

Programming Practices for Readabililty

In general, if you feel like code is not self-describing, the usual solution is to factor it out into a well-named function:

def first(s):
    '''Return the first element from an ordered collection
       or an arbitrary element from an unordered collection.
       Raise StopIteration if the collection is empty.
    '''
    return next(iter(s))

With that helper function, the subsequent code becomes very readable:

>>> extension = {'xml', 'html', 'css', 'php', 'xhmtl'}
>>> one_extension = first(extension)

Patterns for Extracting a Single Value from Collection

The usual ways to get an element from a set, dict, OrderedDict, generator, or other non-indexable collection are:

for value in some_collection:
    break

and:

value = next(iter(some_collection))

The latter is nice because the next() function lets you specify a default value if collection is empty or you can choose to let it raise an exception. The next() function is also explicit that it is asking for the next item.

Alternative Approach

If you actually need indexing and slicing and other sequence behaviors (such as indexing multiple elements), it is a simple matter to convert to a list with list(some_collection) or to use [itertools.islice()][2]:

s = list(some_collection)
print(s[0], s[1])

s = list(islice(n, some_collection))
print(s)

Use popitem(last=False), but keep in mind that it removes the entry from the dictionary, i.e. is destructive.

from collections import OrderedDict
o = OrderedDict()
o['first'] = 123
o['second'] = 234
o['third'] = 345

first_item = o.popitem(last=False)
>>> ('first', 123)

For more details, have a look at the manual on collections. It also works with Python 2.x.

Subclassing and adding a method to OrderedDict would be the answer to clarity issues:

>>> o = ExtOrderedDict(('a',1), ('b', 2))
>>> o.first_item()
('a', 1)

The implementation of ExtOrderedDict:

class ExtOrderedDict(OrderedDict):
    def first_item(self):
        return next(iter(self.items()))

Code that's readable, leaves the OrderedDict unchanged and doesn't needlessly generate a potentially large list just to get the first item:

for item in ordered_dict.items():
    return item

If ordered_dict is empty, None would be returned implicitly.

An alternate version for use inside a stretch of code:

for first in ordered_dict.items():
    break  # Leave the name 'first' bound to the first item
else:
    raise IndexError("Empty ordered dict")

The Python 2.x code corresponding to the first example above would need to use iteritems() instead:

for item in ordered_dict.iteritems():
    return item

The recommended solution next(iter(s)) works for the specific case when you wish to extract the first value of an iterable. If your iterable is of known length, you can generalize to extract the nth value:

def get_nth_item(it, n=0):
    if n < 0:
        n += len(it)
    for index, item in enumerate(it):
        if index == n:
            return item
    raise IndexError(f'Iterable index {n} out of range')

This is a clear way to make intent clear, supporting negative indices and handle errors when an incorrect index is provided. Example usage with OrderedDict:

from collections import OrderedDict

od = OrderedDict([(1, 'a'), (2, 'b'), (3, 'c')])

get_nth_item(od.items(), n=0)   # (1, 'a')
get_nth_item(od.items(), n=-1)  # (3, 'c')
get_nth_item(od.items(), n=3)   # IndexError: Iterable index 3 out of range

First record:

[key for key, value in ordered_dict][0]

Last record:

[key for key, value in ordered_dict][-1]
first = next #<hide this somewhere
first(ordered_dict.iteritems())
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!