It's kind of elegant, but also disgusting, depending on your point of view. :)
import itertools def rangestr(iterable): end = start = iterable.next() for end in iterable: pass return "%s" % start if start == end else "%s-%s" % (start, end) class Rememberer(object): last = None class RangeFinder(object): def __init__(self): self.o = Rememberer() def __call__(self, x): if self.o.last is not None and x != self.o.last + 1: self.o = Rememberer() self.o.last = x return self.o def magic(iterable): return [rangestr(vals) for k, vals in itertools.groupby(sorted(iterable), RangeFinder())] >>> magic([5,7,9,8,6, 21,20, 3,2,1, 22,23, 50]) ['1-3', '5-9', '20-23', '50']
Explanation: it uses itertools.groupby
to group the sorted items together using the key, where the key is a Rememberer
object. The RangeFinder
class saves a Rememberer
object if the sequential heap of elements belongs to one range block. After you left this block, it replaces Rememberer
so that the key does not compare with the equal, and groupby
creates a new group. When groupby
looks at the sorted list, it passes the elements one by one to rangestr
, which builds the line by remembering the first and last element and ignoring everything in between.
Is there a practical reason to use this instead of the 9000 answer ? Probably no; this is basically the same algorithm.
source share