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?
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.
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 :-)
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
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)]
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]]
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
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