HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Finding previous/next item in an OrderedDict

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
itempreviousordereddictnextfinding

Problem

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

Solution

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.

Code Snippets

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

    def next(self):
        ...

    def previous(self):
        ...
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)]

Context

StackExchange Code Review Q#156982, answer score: 3

Revisions (0)

No revisions yet.