A succinct implementation is:
chunker = lambda iterable, n: (ifilterfalse(lambda x: x == (), chunk) for chunk in (izip_longest(*[iter(iterable)]*n, fillvalue=())))
This works because [iter(iterable)]*n
is a list containing the same iterator n times; zipping over that takes one item from each iterator in the list, which is the same iterator, with the result that each zip-element contains a group of n
items.
izip_longest
is needed to fully consume the underlying iterable, rather than iteration stopping when the first exhausted iterator is reached, which chops off any remainder from iterable
. This results in the need to filter out the fill-value. A slightly more robust implementation would therefore be:
def chunker(iterable, n):
class Filler(object): pass
return (ifilterfalse(lambda x: x is Filler, chunk) for chunk in (izip_longest(*[iter(iterable)]*n, fillvalue=Filler)))
This guarantees that the fill value is never an item in the underlying iterable. Using the definition above:
iterable = range(1,11)
map(tuple,chunker(iterable, 3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10,)]
map(tuple,chunker(iterable, 2))
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
map(tuple,chunker(iterable, 4))
[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10)]
This implementation almost does what you want, but it has issues:
def chunks(it, step):
start = 0
while True:
end = start+step
yield islice(it, start, end)
start = end
(The difference is that because islice
does not raise StopIteration or anything else on calls that go beyond the end of it
this will yield forever; there is also the slightly tricky issue that the islice
results must be consumed before this generator is iterated).
To generate the moving window functionally:
izip(count(0, step), count(step, step))
So this becomes:
(it[start:end] for (start,end) in izip(count(0, step), count(step, step)))
But, that still creates an infinite iterator. So, you need takewhile (or perhaps something else might be better) to limit it:
chunk = lambda it, step: takewhile((lambda x: len(x) > 0), (it[start:end] for (start,end) in izip(count(0, step), count(step, step))))
g = chunk(range(1,11), 3)
tuple(g)
([1, 2, 3], [4, 5, 6], [7, 8, 9], [10])