Splitting list based on missing numbers in a sequence

billybandicoot picture billybandicoot · Jun 30, 2010 · Viewed 8.4k times · Source

I am looking for the most pythonic way of splitting a list of numbers into smaller lists based on a number missing in the sequence. For example, if the initial list was:

seq1 = [1, 2, 3, 4, 6, 7, 8, 9, 10]

the function would yield:

[[1, 2, 3, 4], [6, 7, 8, 9, 10]]

or

seq2 = [1, 2, 4, 5, 6, 8, 9, 10]

would result in:

[[1, 2], [4, 5, 6], [8, 9, 10]]

Answer

Mad Scientist picture Mad Scientist · Jun 30, 2010

Python 3 version of the code from the old Python documentation:

>>> # Find runs of consecutive numbers using groupby.  The key to the solution
>>> # is differencing with a range so that consecutive numbers all appear in
>>> # same group.
>>> from itertools import groupby
>>> from operator import itemgetter
>>> data = [ 1,  4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
>>> for k, g in groupby(enumerate(data), lambda i_x: i_x[0] - i_x[1]):
...     print(list(map(itemgetter(1), g)))
...
[1]
[4, 5, 6]
[10]
[15, 16, 17, 18]
[22]
[25, 26, 27, 28]

The groupby function from the itertools module generates a break every time the key function changes its return value. The trick is that the return value is the number in the list minus the position of the element in the list. This difference changes when there is a gap in the numbers.

The itemgetter function is from the operator module, you'll have to import this and the itertools module for this example to work.

Alternatively, as a list comprehension:

>>> [map(itemgetter(1), g) for k, g in groupby(enumerate(seq2), lambda i_x: i_x[0] - i_x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]