split a generator/iterable every n items in python (splitEvery)
Solution 1:
from itertools import islice
def split_every(n, iterable):
i = iter(iterable)
piece = list(islice(i, n))
while piece:
yield piece
piece = list(islice(i, n))
Some tests:
>>> list(split_every(5, range(9)))
[[0, 1, 2, 3, 4], [5, 6, 7, 8]]
>>> list(split_every(3, (x**2 for x in range(20))))
[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81, 100, 121], [144, 169, 196], [225, 256, 289], [324, 361]]
>>> [''.join(s) for s in split_every(6, 'Hello world')]
['Hello ', 'world']
>>> list(split_every(100, []))
[]
Solution 2:
Here's a quick one-liner version. Like Haskell's, it is lazy.
from itertools import islice, takewhile, repeat
split_every = (lambda n, it:
takewhile(bool, (list(islice(it, n)) for _ in repeat(None))))
This requires that you use iter
before calling split_every
.
Example:
list(split_every(5, iter(xrange(9))))
[[0, 1, 2, 3, 4], [5, 6, 7, 8]]
Although not a one-liner, the version below doesn't require that you call iter
which can be a common pitfall.
from itertools import islice, takewhile, repeat
def split_every(n, iterable):
"""
Slice an iterable into chunks of n elements
:type n: int
:type iterable: Iterable
:rtype: Iterator
"""
iterator = iter(iterable)
return takewhile(bool, (list(islice(iterator, n)) for _ in repeat(None)))
(Thanks to @eli-korvigo for improvements.)
Solution 3:
building off of the accepted answer and employing a lesser-known use of iter
(that, when passed a second arg, it calls the first until it receives the second), you can do this really easily:
python3:
from itertools import islice
def split_every(n, iterable):
iterable = iter(iterable)
yield from iter(lambda: list(islice(iterable, n)), [])
python2:
def split_every(n, iterable):
iterable = iter(iterable)
for chunk in iter(lambda: list(islice(iterable, n)), []):
yield chunk