Creating a list using a date as index

python

Solution

Here is a really simple example, basically you are taking parameters passed to `__getitem__` and passing them through a mapping so you can leverage the builting list behaviour.

As you mentioned, `__getslice__` is obsolete, `__getitem__` just needs to notice that it has been passed a `slice` and deal with it appropriately.

import datetime
class DatedList(list):

    def __init__(self, values, dates):
        list.__init__(self, values)
        self.dates = dates
        self._dt_to_idx = {k:v for v,k in enumerate(dates)}

    def __getitem__(self, arg):
        if isinstance(arg, slice):
            start = self._dt_to_idx[arg.start]
            stop = self._dt_to_idx[arg.stop]
            return list.__getitem__(self, slice(start, stop, arg.step))
        else:
            return list.__getitem__(self, self._dt_to_idx[arg])    


dates = [datetime.date(2012,1,x) for x in range(2,6)]
dl = DatedList([1,2,3,4], dates)
print dl[dates[2]]
print dl[dates[1]:dates[3]]

If you need a more complicated mapping - to deal with skipped dates, etc, simply define a method to do the mapping and call that whereever you use `self._dt_to_idx[...]`

Problem

I have made a list that uses a list of dates as an index, like so: ``` >>> import datedlist >>> import datetime >>> dates = [datetime.date(2012,1,x) for x in range(2,6)] >>> values = range(4,8) >>> dates [datetime.date(2012, 1, 2), datetime.date(2012, 1, 3), datetime.date(2012, 1, 4), datetime.date(2012, 1, 5)] >>> dl = datedlist.DatedList(values, dates) >>> dl [4, 5, 6, 7] >>> dl[datetime.date(2012,1,3)] 5 ``` So far all is well, but I also want to be able to use slicing (not extended slicing), like so (the following does not work - it is the result I want): ``` >>> datedlist[datetime.date(2012,1,3):datetime.date(2012,1,4)] [5, 6] ``` Here is my attempt at this (which obviously doesn't work): ``` class DatedList(list): def __init__(self, values, dates): self.dates = dates list.__init__(self, values) def __getitem__(self, date): if isinstance(date, slice): start = self.dates.index(slice[0]) end = self.dates.index(slice[1]) return [list.__getitem__(self, index) for index in range(start, end)] elif isinstance( date, datetime.date ) : index = self.dates.index(date) return list.__getitem__(self, index) elif isinstance(date, int): if date < 0: date += len(self) if date >= len(self): raise IndexError, "index out of range {}".format(date) return list.__getitem__(self, date) else: raise TypeError, "Invalid argument type." ``` The slice[0] and slice[1] are only there to explain my intention. The isinstance(date, int) is only there for debugging - will be removed for production code. Here is the question: how can I implement slicing that uses datetime.date objects as indices? EDIT (after gnibblers 2nd comment): I did try getslice too (even though the docs say that getslice is obsolete). The class then looks like this (isinstance-slice bit commented out because of syntax): ``` class DatedList(list): def __init__(self, values, dates): self.dates = dates list.__init__(self, values) def __contains__(self, date): return date in self.dates def __getslice__(self, fromdate, todate): i_from = self.get_index(fromdate) i_to = self.get_index(todate) print i_from, i_to return [list.__getitem__(self, i) for i in range(i_from, i_to)] def __getitem__(self, date): if isinstance(date, slice): pass # start = self.dates.index(slice[0]) # end = self.dates.index(slice[1]) # return [list.__getitem__(self, i) for i in range(start, end)] elif isinstance(date, datetime.date): index = self.get_index(date) return list.__getitem__(self, index) elif isinstance(date, int): if date < 0: date += len(self) if date >= len(self): raise IndexError, "index out of range {}".format(date) return list.__getitem__(self, date) else: raise TypeError, "Invalid argument type." def get_index(self, date): if date in self.dates: index = self.dates.index(date) elif date < self.dates[0]: index = 0 elif date > self.dates[-1]: index = len(self.dates) - 1 return index ``` The result is: ``` >>> print dl[datetime.date(2012,1,3):datetime.date(2012,1,5)] >>> None ``` Apparently getslice does not get used at all, because the print is not executed. It appears that getitem is executed when a slice is requested, but I don't seem to be able to use a datetime.date in a slice. /EDIT Note: apparently it is not a good idea to subclass list, but none of the alternatives I tried so far seemed to work better (or at all): Building a class from scratch: I could not get the [] notation to work: ``` dl = DatedList(values, dates) value = dl[some_date] # I want this to work value = dl.value(same_date) # I don't want this ``` I have considered using a dict, but my list needs to be ordered and I need to use slicing as well. I also tried to subclass collections.Sequence, but that resulted in: TypeError: descriptor 'init' requires a 'list' object but received a 'DatedList'

Original source