Detectando enteros consecutivos en una lista

Tengo una lista que contiene datos como tales:

[1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14] 

Me gustaría imprimir los rangos de enteros consecutivos:

 1-4, 7-8, 10-14 

¿Hay una forma integrada / rápida / eficiente de hacer esto?

De los documentos :

 >>> 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): ix): ... print map(itemgetter(1), g) ... [1] [4, 5, 6] [10] [15, 16, 17, 18] [22] [25, 26, 27, 28] 

Puede adaptar esto con bastante facilidad para obtener un conjunto impreso de rangos.

Incorporado: No, por lo que yo sé.

Tienes que correr a través de la matriz. Comience con poner el primer valor en una variable e imprímalo, luego, mientras siga presionando el siguiente número, no haga más que recordar el último número en otra variable. Si el siguiente número no está en línea, verifique el último número recordado en comparación con el primer número. Si es lo mismo, no hagas nada. Si es diferente, imprima “-” y el último número. Luego ponga el valor actual en la primera variable y comience de nuevo. Al final de la matriz, ejecuta la misma rutina como si hubiera alcanzado un número fuera de línea.

Podría haber escrito el código, por supuesto, pero no quiero estropear tu tarea 🙂

Esto se imprimirá exactamente como usted especificó:

 >>> nums = [1, 2, 3, 4, 7, 8, 10, 11, 12, 13, 14] >>> ranges = sum((list(t) for t in zip(nums, nums[1:]) if t[0]+1 != t[1]), []) >>> iranges = iter(nums[0:1] + ranges + nums[-1:]) >>> print ', '.join([str(n) + '-' + str(next(iranges)) for n in iranges]) 1-4, 7-8, 10-14 

Si la lista tiene rangos de números únicos, se mostrarán como nn:

 >>> nums = [1, 2, 3, 4, 5, 7, 8, 9, 12, 15, 16, 17, 18] >>> ranges = sum((list(t) for t in zip(nums, nums[1:]) if t[0]+1 != t[1]), []) >>> iranges = iter(nums[0:1] + ranges + nums[-1:]) >>> print ', '.join([str(n) + '-' + str(next(iranges)) for n in iranges]) 1-5, 7-9, 12-12, 15-18 

Una solución corta que funciona sin importaciones adicionales. Acepta cualquier iterable, ordena entradas sin ordenar y elimina elementos duplicados:

 def ranges(nums): nums = sorted(set(nums)) gaps = [[s, e] for s, e in zip(nums, nums[1:]) if s+1 < e] edges = iter(nums[:1] + sum(gaps, []) + nums[-1:]) return list(zip(edges, edges)) 

Ejemplo:

 >>> ranges([2, 3, 4, 7, 8, 9, 15]) [(2, 4), (7, 9), (15, 15)] >>> ranges([-1, 0, 1, 2, 3, 12, 13, 15, 100]) [(-1, 3), (12, 13), (15, 15), (100, 100)] >>> ranges(range(100)) [(0, 99)] >>> ranges([0]) [(0, 0)] >>> ranges([]) [] 

Esto es lo mismo que la solución de @dansalmo que encontré sorprendente, aunque un poco difícil de leer y aplicar (ya que no se ofrece como una función).

Tenga en cuenta que podría modificarse fácilmente para escupir rangos abiertos "tradicionales" [start, end) , por ejemplo, modificando la statement de retorno:

  return [(s, e+1) for s, e in zip(edges, edges)] 

Tuve un problema similar y estoy usando lo siguiente para una lista ordenada. Produce un diccionario con rangos de valores listados en un diccionario. Las teclas separan cada ejecución de números consecutivos y también son el total acumulado de elementos no secuenciales entre números en secuencia.

Su lista me da una salida de {0: [1, 4], 1: [7, 8], 2: [10, 14]}

 def series_dictf(index_list): from collections import defaultdict series_dict = defaultdict(list) sequence_dict = dict() list_len = len(index_list) series_interrupts = 0 for i in range(list_len): if i == (list_len - 1): break position_a = index_list[i] position_b = index_list[i + 1] if position_b == (position_a + 1): sequence_dict[position_a] = (series_interrupts) sequence_dict[position_b] = (series_interrupts) if position_b != (position_a + 1): series_interrupts += 1 for position, series in sequence_dict.items(): series_dict[series].append(position) for series, position in series_dict.items(): series_dict[series] = [position[0], position[-1]] return series_dict 

Usando la operación set, se puede ejecutar el siguiente algoritmo

 def get_consecutive_integer_series(integer_list): integer_list = sorted(integer_list) start_item = integer_list[0] end_item = integer_list[-1] a = set(integer_list) # Set a b = range(start_item, end_item+1) # Pick items that are not in range. c = set(b) - a # Set operation ba li = [] start = 0 for i in sorted(c): end = b.index(i) # Get end point of the list slicing li.append(b[start:end]) # Slice list using values start = end + 1 # Increment the start point for next slicing li.append(b[start:]) # Add the last series for sliced_list in li: if not sliced_list: # list is empty continue if len(sliced_list) == 1: # If only one item found in list yield sliced_list[0] else: yield "{0}-{1}".format(sliced_list[0], sliced_list[-1]) a = [1, 2, 3, 6, 7, 8, 4, 14, 15, 21] for series in get_consecutive_integer_series(a): print series 

Salida para la lista anterior “a”
1-4
6-8
14-15
21

Puede utilizar la biblioteca de colecciones que tiene una clase llamada Contador. El contador puede ser útil si intenta sondear el no de elementos distintos en cualquier iterable

 from collections import Counter data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28] cnt=Counter(data) print(cnt) 

La salida para esto parece

 Counter({1: 1, 4: 1, 5: 1, 6: 1, 10: 1, 15: 1, 16: 1, 17: 1, 18: 1, 22: 1, 25: 1, 26: 1, 27: 1, 28: 1}) 

que al igual que cualquier otro diccionario se puede sondear para los valores clave

Aquí hay otra solución básica sin usar ningún módulo, lo cual es bueno para la entrevista, generalmente en la entrevista que pidieron sin usar ningún módulo:

 #!/usr/bin/python def split_list(n): """will return the list index""" return [(x+1) for x,y in zip(n, n[1:]) if yx != 1] def get_sub_list(my_list): """will split the list base on the index""" my_index = split_list(my_list) output = list() prev = 0 for index in my_index: new_list = [ x for x in my_list[prev:] if x < index] output.append(new_list) prev += len(new_list) output.append([ x for x in my_list[prev:]]) return output my_list = [1, 3, 4, 7, 8, 10, 11, 13, 14] print get_sub_list(my_list) 

Salida:

 [[1], [3, 4], [7, 8], [10, 11], [13, 14]]