How to Create a Circular List in Python


The challenge

Create a Circular List

A circular list is of finite size, but can infititely be asked for its previous and next elements. This is because it acts like it is joined at the ends and loops around.

For example, imagine a CircularList of [1, 2, 3, 4]. Five invocations of next() in a row should return 1, 2, 3, 4 and then 1 again. At this point, five invocations of prev() in a row should return 4, 3, 2, 1 and then 4 again.

Your CircularList is created by passing a vargargs parameter in, e.g. new CircularList(1, 2, 3). Your list constructor/init code should throw an Exception if nothing is passed in.

The solution in Python code

Option 1:

class CircularList():
    def __init__(self, *args):
        self.l=[v for v in args]
        if not self.l:
            raise Exception("")
        self.i=-1
        
    def next(self):
        self.i=self.i+1
        self.i=self.i if len(self.l)>self.i else 0
        return self.l[self.i]
    
    def prev(self):
        self.i=self.i-1
        self.i=self.i if self.i>=0 else len(self.l)-1
        return self.l[self.i]

Option 2:

class CircularList(list):
    def __init__(self, *args):
        if not args: raise
        self.lst = args
        
    def next(self):
        try: self.i += 1
        except: self.i = 0
        return self.lst[self.i%len(self.lst)]
    
    def prev(self):
        try: self.i -=1
        except: self.i = -1
        return self.lst[self.i%len(self.lst)]

Option 3:

class CircularList():
    def __init__(self, *args):
        if not args:
            raise ValueError
        self.arg = args
        self.index = None
        self.max_index = len(args) - 1
        
    def next(self):
        if self.index is None or self.index == self.max_index:
            self.index = 0
        else:
            self.index += 1        
        return self.arg[self.index]
    
    def prev(self):
        if self.index is None or self.index == 0:
            self.index = self.max_index
        else:
            self.index -= 1
        return self.arg[self.index]

Test cases to validate our solution

import test
import solution # or from solution import example

# test.assert_equals(actual, expected, [optional] message)
@test.describe("Fixed Tests")
def test_group():
    @test.it("test circular string list")
    def test_case():
        list = CircularList("one", "two", "three")
        test.assert_equals(list.next(), "one")
        test.assert_equals(list.next(), "two")
        test.assert_equals(list.next(), "three")
        test.assert_equals(list.next(), "one")
        test.assert_equals(list.prev(), "three")
        test.assert_equals(list.prev(), "two")
        test.assert_equals(list.prev(), "one")
        test.assert_equals(list.prev(), "three")
        
    @test.it("test circular integer list")
    def test_case_2():
        list = CircularList(1, 2, 3, 4, 5)
        test.assert_equals(list.prev(), 5)
        test.assert_equals(list.prev(), 4)
        test.assert_equals(list.next(), 5)
        test.assert_equals(list.next(), 1)
        test.assert_equals(list.prev(), 5)
        test.assert_equals(list.prev(), 4)
        test.assert_equals(list.prev(), 3)
        test.assert_equals(list.prev(), 2)