web-dev-qa-db-fra.com

Détecter des entiers consécutifs dans une liste

J'ai une liste contenant des données en tant que telles:

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

Je voudrais imprimer les plages d'entiers consécutifs:

1-4, 7-8, 10-14

Y at-il un moyen intégré/rapide/efficace de le faire?

44
James

De les docs :

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

Vous pouvez l'adapter assez facilement pour obtenir un ensemble imprimé de plages.

68
Dominic Rodger

Built-In: Non, autant que je sache.

Vous devez parcourir le tableau. Commencez par mettre la première valeur dans une variable et imprimez-la, puis, tant que vous continuez à frapper le nombre suivant, ne faites que mémoriser le dernier nombre d'une autre variable. Si le numéro suivant n'est pas en ligne, vérifiez le dernier numéro mémorisé par rapport au premier numéro. Si c'est pareil, ne faites rien. Si c'est différent, écrivez "-" et le dernier numéro. Ensuite, mettez la valeur actuelle dans la première variable et recommencez… .. À la fin du tableau, vous exécutez la même routine que si vous aviez frappé un nombre hors de la ligne.

J'aurais pu écrire le code, bien sûr, mais je ne veux pas gâcher vos devoirs :-)

3
TToni

Cela imprimera exactement comme vous l'avez spécifié:

>>> 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 liste comporte des tranches de numéros uniques, elles seront affichées sous la forme n-n:

>>> 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
2
dansalmo

Une solution courte qui fonctionne sans importations supplémentaires. Il accepte toutes les itérations, trie les entrées non triées et supprime les éléments en double:

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))

Exemple:

>>> 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([])
[]

C’est la même chose que la solution de @ dansalmo que j’ai trouvée incroyable, mais un peu difficile à lire et à appliquer (car elle n’a pas été donnée en fonction).

Notez qu'il pourrait facilement être modifié pour cracher des plages ouvertes "traditionnelles" [start, end), par exemple. modifier l'instruction de retour:

    return [(s, e+1) for s, e in Zip(edges, edges)]
2
coldfix

Voici une autre solution de base sans utiliser de module, ce qui est bon pour un entretien, généralement dans l'entretien demandé sans utiliser aucun module:

#!/usr/bin/python

def split_list(n):
    """will return the list index"""
    return [(x+1) for x,y in Zip(n, n[1:]) if y-x != 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)

Sortie:

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

J'ai eu un problème similaire et j'utilise ce qui suit pour une liste triée. Il génère un dictionnaire avec des plages de valeurs répertoriées dans un dictionnaire. Les touches séparent chaque série de numéros consécutifs et représentent également le total cumulé des éléments non séquentiels entre les numéros en séquence. 

Votre liste me donne une sortie 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
0
12345678910111213

En utilisant l'opération set, l'algorithme suivant peut être exécuté

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 b-a

    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

Sortie pour la liste ci-dessus "a" 
1-4 
6-8 
14-15 
21 

0
theBuzzyCoder