9
\$\begingroup\$

I have an OrderedDict and, given a key in it, need to find the previous and next keys accordingly. The implementation should wrap (i.e. if the given key is the first in the dictionary, the previous key should be the last one and vice versa).

Here's what I've come up with so far (adapted from production code) which works, but seems a bit... clunky. Any tips for improvement would be appreciated!

from collections import OrderedDict


# Create a sample
a_dict = OrderedDict()
for n in range(25):
    a_dict.update({n: n + 1})

# The currently-selected key (not a plain int in my actual production code)
selected = 0

the_keys = list(a_dict.keys())
# Only find the prev/next item if we have more than one key
if len(the_keys) <= 1:
    return

current = None
for index, key in enumerate(the_keys):
    if key == selected:
        current = index
        break

# For the majority of cases
prev = current - 1
next = current + 1

# For the edge cases (ends of the list)
if current == 0:
    prev = -1
    next = 1
elif current == len(the_keys) - 1:
    prev = current - 1
    next = 0
\$\endgroup\$
  • 3
    \$\begingroup\$ This question seems a bit abstract. What are you really trying to accomplish, and is an OrderedDict the right data structure for it? \$\endgroup\$ – 200_success Mar 5 '17 at 18:02
  • \$\begingroup\$ I have a collection of domain-specific objects which need to be presented in a GUI. The OrderedDict stores those objects, with the keys of the dictionary set to the control IDs used by the GUI toolkit. I'm using an OrderedDict so that the objects are added to the GUI in the right order, and want to obtain the previous and next object at any given moment so that when the user presses left or right arrow, the view can cycle between them. \$\endgroup\$ – James Scholes Mar 5 '17 at 18:05
2
\$\begingroup\$

One way of simplifying the problem is to provide a thin wrapper around the OrderedDict. Something along the lines of:

class DictCycler:
    def __init__(self, data):
        self.data = data
        ...

    def next(self):
        ...

    def previous(self):
        ...

An other way is to make use of itertools capability. Especially cycle and islice. The idea being that, using cycle, cycling through the elements in the proper order is straightforward, and cycling in reverse can be done by going forward N - 2 times (where N is the length of the dictionary) with the help of islice. This yield:

import itertools


class DictCycler:
    def __init__(self, data):
        self.data = data
        self.iterator = itertools.cycle(data)
        self.jump_to_previous = len(data) - 2

    def next(self):
        return self.data[next(self.iterator)]

    def previous(self):
        prev = itertools.islice(self.iterator, self.jump_to_previous, None)
        return self.data[next(prev)]

You then just have to populate your dictionary and freeze it into this wrapper.

If you need to be able to modify the dictionary contained in data after passing it to this class, you will need to adapt the recipe a bit.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.