HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

Grouping consecutive numbers into ranges in Python 3.2

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
groupingintonumbersrangespythonconsecutive

Problem

The following is a function that I wrote to display page numbers as they appear in books.

If you enter the list [1,2,3,6,7,10], for example, it would return:

1-3,6-7,10


This seemed like a nice example of how the dictionary data type can be used in Python.

Is there is an even more code-efficient way to do this?

def formatpagelist (numberlist):
    tempdic={}
    returnstring=''

    for number in numberlist:
        if number-1 in tempdic.keys():
            tempdic[number-1]=number
        elif number-1 in tempdic.values():
            for key in tempdic.keys():
                if number-1==tempdic[key]: foundkey=key
            tempdic[foundkey]=number
        else:
            tempdic[number]=0

    keylist=list(tempdic.keys())
    keylist.sort()

    for key in keylist:
        if tempdic[key]>0:
            returnstring+=(str(key)+'-'+str(tempdic[key])+',')
        else: returnstring+=str(key)+','

    return returnstring

Solution

You could use this one-liner to generate groups of consecutive integers in a list:

from itertools import groupby, count

groupby(numberlist, lambda n, c=count(): n-next(c))


Then to finish it off, generate the string from the groups.

def as_range(iterable): # not sure how to do this part elegantly
    l = list(iterable)
    if len(l) > 1:
        return '{0}-{1}'.format(l[0], l[-1])
    else:
        return '{0}'.format(l[0])

','.join(as_range(g) for _, g in groupby(numberlist, key=lambda n, c=count(): n-next(c)))
# '1-3,6-7,10'


This assumes they are in sorted order and there are no duplicates. If not sorted, add a sorted() call on numberlist beforehand. If there's duplicates, make it a set beforehand.

Code Snippets

from itertools import groupby, count

groupby(numberlist, lambda n, c=count(): n-next(c))
def as_range(iterable): # not sure how to do this part elegantly
    l = list(iterable)
    if len(l) > 1:
        return '{0}-{1}'.format(l[0], l[-1])
    else:
        return '{0}'.format(l[0])

','.join(as_range(g) for _, g in groupby(numberlist, key=lambda n, c=count(): n-next(c)))
# '1-3,6-7,10'

Context

StackExchange Code Review Q#5196, answer score: 18

Revisions (0)

No revisions yet.