Python has a range
method, which allows for stuff like:
>>> range(1, 6)
[1, 2, 3, 4, 5]
What I’m looking for is kind of t
This is kind of elegant but also kind of 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 elements together by a key, where the key is a Rememberer
object. The RangeFinder
class keeps a Rememberer
object as long as a consecutive bunch of items belongs to the same range block. Once you've passed out of a given block, it replaces the Rememberer
so that the key won't compare equal and groupby
will make a new group. As groupby
walks over the sorted list, it passes the elements one-by-one into rangestr
, which constructs the string by remembering the first and the last element and ignoring everything in between.
Is there any practical reason to use this instead of 9000's answer? Probably not; it's basically the same algorithm.