A way that is more efficient than fully constructing the sorted list (and should work for arbitrary iterable types, containing any data types that allow for greater-than comparisons):
def len_ascending_run_from_start(seq):
vals = enumerate(seq)
try:
c,x = 1, vals.next()[1]
except StopIteration:
raise ValueError("Sequence is empty!")
for i, cur_x in vals:
if cur_x < x:
break
c,x = c+1, cur_x
return c
E.g.:
In [44]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,3), datetime.date(2012,1,5)])
Out[44]: 1
In [45]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,6), datetime.date(2012,1,5)])
Out[45]: 2
In [46]: len_ascending_run_from_start([datetime.date(2012,1,4), datetime.date(2012,1,6), datetime.date(2012,1,7)])
Out[46]: 3
In [47]: len_ascending_run_from_start((1,2,3,4))
Out[47]: 4
In [48]: len_ascending_run_from_start((1,3,2,4))
Out[48]: 2
In [49]: len_ascending_run_from_start(set([1,3,2,4]))
Out[49]: 4
It could also be fun / useful to make a class such that len
automatically reports this statistic about the underlying sequence (and keeps a simplistic cache of the result, to avoid calculating it repeatedly).
class AscendingRunFromStart(object):
def __init__(self, seq):
self.seq = seq
def __repr__(self):
return self.seq.__repr__()
__str__ = __repr__
def __len__(self):
if hasattr(self, "_len"):
return self._len
vals = enumerate(self.seq)
c,x = 1, vals.next()[1]
for i, cur_x in vals:
if cur_x < x:
break
c,x = c+1, cur_x
self._len = c
return self._len
E.g.:
In [76]: x = AscendingRunFromStart([1,3,2,4])
In [77]: x
Out[77]: [1, 3, 2, 4]
In [78]: len(x)
Out[78]: 2
A good extension would be to use a Descriptor pattern to make the seq
attribute of the class read-only, or to invoke an updated len
calculation whenever set
is called. (Left as exercise for the reader...)