Critique des autres réponses ici:
Aucune de ces réponses n'est des morceaux de taille égale, ils laissent tous un morceau de runt à la fin, donc ils ne sont pas complètement équilibrés. Si vous utilisiez ces fonctions pour distribuer le travail, vous avez intégré la perspective que l'une finisse probablement bien avant les autres, donc elle ne ferait rien pendant que les autres continueraient à travailler dur.
Par exemple, la première réponse actuelle se termine par:
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]
Je déteste juste ce runt à la fin!
D' autres, comme list(grouper(3, xrange(7)))
, et à la chunk(xrange(7), 3)
fois: retour [(0, 1, 2), (3, 4, 5), (6, None, None)]
. Ce None
ne sont que du rembourrage, et plutôt inélégant à mon avis. Ils ne coupent PAS uniformément les itérables.
Pourquoi ne pouvons-nous pas mieux les répartir?
Ma (mes) solution (s)
Voici une solution équilibrée, adaptée d'une fonction que j'ai utilisée en production (Note en Python 3 à remplacer xrange
par range
):
def baskets_from(items, maxbaskets=25):
baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
for i, item in enumerate(items):
baskets[i % maxbaskets].append(item)
return filter(None, baskets)
Et j'ai créé un générateur qui fait de même si vous le mettez dans une liste:
def iter_baskets_from(items, maxbaskets=3):
'''generates evenly balanced baskets from indexable iterable'''
item_count = len(items)
baskets = min(item_count, maxbaskets)
for x_i in xrange(baskets):
yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]
Et enfin, puisque je vois que toutes les fonctions ci-dessus renvoient des éléments dans un ordre contigu (comme ils ont été donnés):
def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
'''
generates balanced baskets from iterable, contiguous contents
provide item_count if providing a iterator that doesn't support len()
'''
item_count = item_count or len(items)
baskets = min(item_count, maxbaskets)
items = iter(items)
floor = item_count // baskets
ceiling = floor + 1
stepdown = item_count % baskets
for x_i in xrange(baskets):
length = ceiling if x_i < stepdown else floor
yield [items.next() for _ in xrange(length)]
Production
Pour les tester:
print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))
Qui imprime:
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]
Notez que le générateur contigu fournit des morceaux dans les mêmes modèles de longueur que les deux autres, mais les éléments sont tous en ordre et ils sont aussi uniformément divisés que l'on peut diviser une liste d'éléments discrets.