I sometimes find myself in a situation - similar to the following hypothetical - but I can't seem to come up with a satisfactory, Pythonic solution:
Let's say we have a list of numbers:
numbers = [1, 2, 3, 5, 6, 8, 9]
We would like to group the numbers into sub-lists, such that adjacent numbers with an absolute difference of exactly 1
will be part of the same group. The desired result in this case would be:
grouped_numbers = [[1, 2, 3], [5, 6], [8, 9]]
On the surface, it seems like itertools.groupby
is the tool for the job. However, this grouping operation is different from the average, trivial application of itertools.groupby
, because the key
(the callable that returns the distinct "category-objects", which correspond to the distinct groups that a given item should belong to) must retain some kind of state between invocations by itertools.groupby
because whether the "current" group terminates or continues depends on the previous item.
One way to achieve this is by using persistent default-arguments. This solution works, but it feels really kludgy:
from itertools import groupby
def key(number, prev_number=[None], prev_key=[object()]):
if prev_number[0] is None or abs(number - prev_number[0]) != 1:
prev_key[0] = object()
prev_number[0] = number
return prev_key[0]
print([list(group) for _, group in groupby(numbers, key=key)])
Surely, more Python solutions must exist, and my question is: What are they? I would prefer solutions using itertools
recipes, or anything that's available in the standard library.