Stack Exchange
Group consecutive integers together
Have the following code:
import sys


ints = [1,2,3,4,5,6,8,9,10,11,14,34,14,35,16,18,39,10,29,30,14,26,64,27,48,65]
ints.sort()
ints = list(set(ints))

c = {}

for i,v in enumerate(ints):

    if i+1 >= len(ints):
        continue

    if ints[i+1] == v + 1 or ints[i-1] == v - 1:

        if len(c) == 0:
            c[v] = [v]
            c[v].append(ints[i+1])
        else:
            added=False
            for x,e in c.items():
                last = e[-1]
                if v in e:
                    added=True
                    break

                if v - last == 1:
                    c[x].append(v)
                    added=True

            if added==False:
                c[v] = [v]
    else:
        if v not in c:
            c[v] = [v]



print('input ', ints)
print('output ', c))

The objective:
Given a list of integers, create a dictionary that contains consecutive integers grouped together to reduce the overall length of the list.
Here is output from my current solution:
input  [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]
output  {1: [1, 2, 3, 4, 5, 6], 8: [8, 9, 10, 11], 14: [14], 16: [16], 18: [18], 26: [26, 27], 29: [29, 30], 34: [34, 35], 39: [39], 48: [48], 64: [64]}

Conditions/constraints:

If the current integer is either a) in an existing list or b) is the last item in an existing list, we don't want to create another list for this item. 
i.e. in the range 1-5 inclusive, when we get to 3, don't create a list 3,4, instead append 3 to the existing list [1,2]

My current iteration works fine, but it gets exponentially slower the bigger the list is because of the for x,e in c.items() existing list check. 
How can I make this faster while still achieving the same result?
New solution (from 13 seconds to 0.03 seconds using an input list of 19,000 integers):
c = {}

i = 0

last_list = None

while i < len(ints):
    cur = ints[i]

    if last_list is None:
        c[cur] = [cur]
        last_list = c[cur]

    else:

        if last_list[-1] == cur-1:
            last_list.append(cur)
        else:
            c[cur] = [cur]
            last_list = c[cur]

    i += 1


You're working with a sorted list of integers. So you know that the next number you look at will be greater than the last number. So it can either go in the most recent list of consecutive ints, or it's going to start a new list. There's no reason for the for x,e in c.items() if you just keep track of the one you were most recently working with.

but how do i know if it goes in the most recent list or to start a new list? i.e. 1,2,3,8.. just because 8 > 3 doesn't mean it goes in the 1,2,3 list. don't i still need to check if 8 - last integer == 1?

Sort after calling list(set(...))

Yes, you still have to check. But you only have to check the last list you were working with.

@sk099. So check it...

Your proposed output is missing 65

hi, i figured it out. see edited question

@sk099. Why are you shuffling things around so much? Why not use a for loop and have you actually tried ruining your update? You never seem to create a new list...

in the else block, c[cur] = [cur] creates the new list.

As you have lists of consecutive numbers, I suggest you to use range objects instead of lists:
d, head = {}, None
for x in l:
    if head is None or x != d[head].stop:
        head = x
    d[head] = range(head, x+1)


The solution is simple if you use a for loop and just keep track of your current list. Don't forget to make a new list when you find a gap:
result = {}
cl = None
for i in ints:
    if cl is None or i - 1 != cl[-1]:
        cl = result.setdefault(i, [])
    cl.append(i)


There is a great library called more_itertools which has a method called: consecutive_groups():
import more_itertools as mit
x = [1,2,3,4,5,6,8,9,10,11,14,34,14,35,16,18,39,10,29,30,14,26,64,27,48,65]
x = [list(j) for j in mit.consecutive_groups(sorted(list(set(x))))]
# [[1, 2, 3, 4, 5, 6], [8, 9, 10, 11], [14], [16], [18], [26, 27], [29, 30], [34, 35], [39], [48], [64, 65]]
dct_x = {i[0]: i for i in x}
print(dct_x)

Output:
{1: [1, 2, 3, 4, 5, 6], 8: [8, 9, 10, 11], 14: [14], 16: [16], 18: [18], 26: [26, 27], 29: [29, 30], 34: [34, 35], 39: [39], 48: [48], 64: [64, 65]}

One more comment, you want to sort after converting to and from a set, since sets are unordered.

even if i sort before making it into a set? or is it just to be safe?

If you sort before turning it into a set, then there is no guaranteed when you convert back to a list it will be sorted.

I need to start exploring this module some more.  Thanks chrisz!

One can solve this task in O(n) (linear) complexity. Just keep it simple:
integers = [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]

helper = []
counter = 0
while counter < len(integers):
  if not helper or helper[-1] + 1 != integers[counter]:
    print('gap found', integers[counter])  # do your logic
  helper.append(integers[counter])
  counter += 1

The algorithm above assumes that the input list is already sorted. It gives us a huge advantage. At the same time one can sort the list of integers explicitly before running this algorithm. The total complexity of the solution will be then: O(n * log n) + O(n) which is efficiently O(n * log n). And O(n * log n) is the complexity of the sorting procedure.
I would kindly suggest to remember this extremely useful trick of using sorting before approaching a task for future usages.

This doesn't solve the problem at all, unfortunately. The idea is to keep a dictionary of lists where the keys of the dictionary is the start of a consecutive range of numbers. If the next number in the iteration can't be added to the last list i.e. current_integer - last_integer == 1, then create a new dict entry with that int as the key

I don't think that giving a full solution for the problem is a good idea. Just wanted to give you a hint how to approach this and similar problems in the future.

Change the while to for counter, number in enumerate(ints):

@MadPhysicist sure, it will work, but I just tried to avoid all python-specific things.

The condition result[-1] != integers[counter] should be result[-1] != integers[counter] - 1, or in my suggested notation, result[-1] != number - 1

You don't even need counter for number in integers is not Python specific.

Here's a simple implementation that achieves what you are after, using list slicing:
integers = [1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]
for i, integer in enumerate(integers):
    if i == 0:
        out_dict = {}
        start = 0
    else:
        if integer != prev_integer + 1:
            out_dict[integers[start]] = integers[start:i]
            start = i
        if i == len(integers) - 1:
            out_dict[integers[start]] = integers[start:]
    prev_integer = integer

>>>out_dict =  {1: [1, 2, 3, 4, 5, 6], 8: [8, 9, 10, 11], 14: [14], 16: [16], 18: [18], 26: [26, 27], 29: [29, 30], 34: [34, 35], 39: [39], 48: [48], 64: [64]}

Note: The dictionary will likely not be sorted by ascending keys, as dict types are not ordered.

You can try with itertools , But i would like to try recursion :
input_dta=[1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 14, 16, 18, 26, 27, 29, 30, 34, 35, 39, 48, 64, 65]

final_=[]

def consecutives(data):
    sub_final=[]

    if not data:
        return 0
    else:
        for i,j in enumerate(data):
            try:

                if abs(data[i]-data[i+1])==1:
                    sub_final.extend([data[i],data[i+1]])



                else:
                    if sub_final:
                        final_.append(set(sub_final))




                    return consecutives(data[i+1:])
            except IndexError:
                pass

    final_.append(set(sub_final))


consecutives(input_dta)



print(final_)

output:
[{1, 2, 3, 4, 5, 6}, {8, 9, 10, 11}, {26, 27}, {29, 30}, {34, 35}, {64, 65}]

