Text Indexed List for Python


Text Indexed List for Python

While working on a project, I came across the need to have a list, that I can access via string indexes. Just to be clear, I need to maintain the order of the items, so a dictionary doesn’t work. I also need the ability to add, remove, and move items in this list by their indexes, so an OrderedDict object also doesn’t work. Thus I created a new collection called a Text Indexed list.

The main data structure of this would be a list of lists, where each child list would be a string and an object. Since it needs to be mutable, I didn’t set this as a tuple. In notation, this is List[List[str, Any]] (using objects from the typing library).

This is all well and good, but i would of course need to manipulate selected items in this list. In fact, most of the tasks I could think of involved this, so I created a separate index, so I could locate items a lot faster.

from typing import Any, List


class TextIndexedList:

def __init__(self, index_list: List[List[str, Any]]=None):

self.index = []
if index_list:
self.indexed_list = index_list
self.__build_index__()
else:
self.indexed_list = []

def __build_index__(self):
self.index = [x[0] for x in self.indexed_list]

Notice I used a list comprehension to create the index, so it would work faster.

So now we need the standard get method. This is done by writing a __getitem__ method. Using this method, we can get items as we would with a dictionary doing object[“index”].

from typing import Any, List


class TextIndexedList:

def __init__(self, index_list: List[List[str, Any]]=None):

self.index = []
if index_list:
self.indexed_list = index_list
self.__build_index__()
else:
self.indexed_list = []

def __build_index__(self):
self.index = [x[0] for x in self.indexed_list]

def __getitem__(self, index: str) -> bool:
key = self.index.index(index)
if key is not None:
return self.indexed_list[key][1]
return False

Then I added the basics for Dictionaries, an iterator method and a keys method.

from typing import Any, List


class TextIndexedList:

def __init__(self, index_list: List[List[str, Any]]=None):

self.index = []
if index_list:
self.indexed_list = index_list
self.__build_index__()
else:
self.indexed_list = []

def __build_index__(self):
self.index = [x[0] for x in self.indexed_list]

def __getitem__(self, index: str) -> bool:
key = self.index.index(index)
if key is not None:
return self.indexed_list[key][1]
return False

def __iter__(self) -> list:
return self.indexed_list

def keys(self) -> list:
return self.index

Notice the __iter__ method just needs to return an object that already has an iterator set-up, so returning the data list is all that is needed.

Lastly, I added the relevant methods needed to manage the list. I decided to stick with the method names already used by lists and added a couple more. You should also note that I have set the relevant methods to return a boolean value rather than raising exceptions. I know this is not really pyronic, but I find it easier to use than having to put in a lot of try excepts into the code using the class.

from typing import Any, List


class TextIndexedList:

def __init__(self, index_list: List[List[str, Any]]=None):

self.index = []
if index_list:
self.indexed_list = index_list
self.__build_index__()
else:
self.indexed_list = []

def __build_index__(self):
self.index = [x[0] for x in self.indexed_list]

def __getitem__(self, index: str) -> bool:
key = self.index.index(index)
if key is not None:
return self.indexed_list[key][1]
return False


def __iter__(self) -> list:
return self.indexed_list

def keys(self) -> list:
return self.index

def append(self, index: str, item: Any) -> bool:
if index not in self.index:
self.indexed_list.append([index, item])
self.__build_index__()
return True
return False

def replace(self, index: str, item: any) -> bool:
key = self.index.index(index)
if key is not None:
self.indexed_list[key] = [index, item]
self.__build_index__()
return True
return False

def remove(self, index: str) -> bool:
key = self.index.index(index)
if key is not None:
del self.indexed_list[key]
self.__build_index__()
return True
return False

def insert(self, before_index: str, index: str, item: Any) -> bool:
key = self.index.index(before_index)
if key is not None:
self.indexed_list.insert(key, [index, item])
self.__build_index__()
return True
return False

def move(self, index: str, before_index: str) -> bool:
key = self.index.index(index)
before_key = self.index.index(before_index)
if key is not None and before_key is not None:
item = self.indexed_list.pop(key)
self.indexed_list.insert(before_key, item)
self.__build_index__()
return True
return False

This is not the most complex but of code in the world, but its a collection object that I often find I want to use.