# grouping a flat list of number by range

John Machin sjmachin at lexicon.net
Fri Jun 2 01:35:37 CEST 2006

```On 2/06/2006 8:36 AM, Paddy wrote:
> joh12005 at yahoo.fr wrote:
>> hello,
>>
>> i'm looking for a way to have a list of number grouped by consecutive
>> interval, after a search, for example :
>>
>> [3, 6, 7, 8, 12, 13, 15]
>>
>> =>
>>
>> [[3, 4], [6,9], [12, 14], [15, 16]]
>>
>> (6, not following 3, so 3 => [3:4] ; 7, 8 following 6 so 6, 7, 8 =>
>> [6:9], and so on)
>>
>> i was able to to it without generators/yield but i think it could be
>> better with them, may be do you an idea?
>>
>> best regards,
>>
>> J.
>
> I did a proceedural version, then re-read your post and did a generator
> based version ;-)
>
> === interv1 ===
>>>> inlist = [3, 6, 7, 8, 12, 13, 15]
>>>> tmp = []
>>>> for i,val in enumerate(inlist):
> ... 	if i==0:
> ... 		tmp.append(val)
> ... 	elif val != valinc:
> ... 		tmp += [valinc, val]
> ... 	valinc = val+1
> ...
>>>> tmp.append(valinc)
>>>> tmp
> [3, 4, 6, 9, 12, 14, 15, 16]
>>>> tmp[0::2]
> [3, 6, 12, 15]
>>>> tmp[1::2]
> [4, 9, 14, 16]
>>>> zip(tmp[0::2], tmp[1::2])
> [(3, 4), (6, 9), (12, 14), (15, 16)]
>
> === END interv1 ===
>
> === interv2 ===
>>>> def interv2(inlist):
> ... 	for i,val in enumerate(inlist):
> ... 		if i==0:
> ... 			tmp = val
> ... 		elif val != valinc:
> ... 			yield [tmp, valinc]
> ...                     tmp = val
> ... 		valinc = val+1
> ... 	yield [tmp, valinc]
> ...
>>>> list(interv2(inlist))
> [[3, 4], [6, 9], [12, 14], [15, 16]]
>
> === END interv2 ===
>
>

Oh the siren call of every new feature in the language!
enumerate() just to get a first-time test, and then botch it??

Read the following; the replacement version uses a simple old-fashioned
inelegant flag, works with an empty sequence, and doesn't depend on the
input being sliceable or indexable.

HTH,
John

C:\junk>type interval.py
def interv2(inlist):
for i,val in enumerate(inlist):
if i==0:
tmp = val
elif val != valinc:
yield [tmp, valinc]
tmp = val
valinc = val+1
yield [tmp, valinc]

def interv3(inseq):
first = True
for val in inseq:
if first:
tmp = val
first = False
elif val != valinc:
yield [tmp, valinc]
tmp = val
valinc = val + 1
if not first:
yield [tmp, valinc]

tests = [
[3, 4, 6, 9, 12, 14, 15, 16],
[3, 3, 3],
[],
]

for func in (interv3, interv2):
for test in tests:
print "%s(%s) ->" % (func.__name__, test)
result = list(func(test))
print "\t%r" % result

C:\junk>interval.py
interv3([3, 4, 6, 9, 12, 14, 15, 16]) ->
[[3, 5], [6, 7], [9, 10], [12, 13], [14, 17]]
interv3([3, 3, 3]) ->
[[3, 4], [3, 4], [3, 4]]
interv3([]) ->
[]
interv2([3, 4, 6, 9, 12, 14, 15, 16]) ->
[[3, 5], [6, 7], [9, 10], [12, 13], [14, 17]]
interv2([3, 3, 3]) ->
[[3, 4], [3, 4], [3, 4]]
interv2([]) ->
Traceback (most recent call last):
File "C:\junk\interval.py", line 33, in ?
result = list(func(test))
File "C:\junk\interval.py", line 9, in interv2
yield [tmp, valinc]
UnboundLocalError: local variable 'tmp' referenced before assignment

C:\junk>

```