39

I found the following code that is compatible with python2

from itertools import izip_longest
def grouper(n, iterable, padvalue=None):
  "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
  return izip_longest(*[iter(iterable)]*n, fillvalue=padvalue)

However, this isn't working with Python 3. I get the following error

ImportError: cannot import name izip_longest

Can someone help?

I'd like to convert my list of [1,2,3,4,5,6,7,8,9] to [[1,2,3],[4,5,6],[7,8,9]]

Edit

Now Python3 compatible

Code below is adapted from the selected answer. Simply change name from izip_longest to zip_longest.

from itertools import zip_longest
def grouper(n, iterable, padvalue=None):
  "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
  return zip_longest(*[iter(iterable)]*n, fillvalue=padvalue)
Jose Luis
  • 3,307
  • 3
  • 36
  • 53
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    Read the docs! The Python 3 version of the `itertools` module documentation has the updated recipe for `grouper`: http://docs.python.org/py3k/library/itertools.html#itertools-recipes – Ned Deily May 02 '11 at 01:36
  • Does this answer your question? [How do you split a list into evenly sized chunks?](https://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks) – Tomerikoo Feb 09 '22 at 07:22

5 Answers5

56

In Python 3's itertools there is a function called zip_longest. It should do the same as izip_longest from Python 2.

Why the change in name? You might also notice that itertools.izip is now gone in Python 3 - that's because in Python 3, the zip built-in function now returns an iterator, whereas in Python 2 it returns a list. Since there's no need for the izip function, it also makes sense to rename the _longest variant for consistency.

Ben James
  • 121,135
  • 26
  • 193
  • 155
3

After all that discussion above, here's a python3 solution that I believe gives safer, more predicable results.

def chunker(iter, size):
    chunks = [];
    if size < 1:
        raise ValueError('Chunk size must be greater than 0.')
    for i in range(0, len(iter), size):
        chunks.append(iter[i:(i+size)])
    return chunks

example = [1,2,3,4,5,6,7,8,9]
print(' 1: ' + str(chunker(example, 1)))
print(' 3: ' + str(chunker(example, 3)))
print(' 4: ' + str(chunker(example, 4)))
print(' 8: ' + str(chunker(example, 8)))
print(' 9: ' + str(chunker(example, 9)))
print('10: ' + str(chunker(example, 10)))

The results are:

$ python3 iter_chunk.py 
 1: [[1], [2], [3], [4], [5], [6], [7], [8], [9]]
 3: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
 4: [[1, 2, 3, 4], [5, 6, 7, 8], [9]]
 8: [[1, 2, 3, 4, 5, 6, 7, 8], [9]]
 9: [[1, 2, 3, 4, 5, 6, 7, 8, 9]]
10: [[1, 2, 3, 4, 5, 6, 7, 8, 9]]
JasonGabler
  • 570
  • 5
  • 9
2

According to the doc:

>>> s = [1,2,3,4,5,6,7,8,9]
>>> n = 3
>>> list(zip(*[iter(s)]*n))
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Julien Palard
  • 8,736
  • 2
  • 37
  • 44
  • 2
    Be aware that `len(s) % n != 0` will result in missing members as the given recipe does not handle remainders. For example, `n = 8` will result in `[(1, 2, 3, 4), (5, 6, 7, 8)]`. Where's 9? In the same vein, : `n > len(s)` will result in `[]`. – JasonGabler Oct 11 '18 at 21:14
  • 2
    Would work by replacing zip with itertools.zip_longest as necesarry. – Julien Palard Oct 11 '18 at 22:01
  • 2
    With `n = 8` and itertools.zip_longest, we get`[(1, 2, 3, 4), (5, 6, 7, 8), (9, None, None, None)]`. We do get the 9 back, but now you must mitigate `None`s. And, with `n > len(s)` we are still left with `[]`. PHP's implementation of `array_chunk()` always seemed intuitive, where you get back (in Python parlance) for `n = 8`, `[(1, 2, 3, 4), (5, 6, 7, 8), (9)]` and for `n > len(s)` you get back the original array as the sole member of an outer array. This way you always end up with something you can safely put through two nested loops without much caveating. – JasonGabler Oct 12 '18 at 23:20
  • 2
    @JasonGabler: zip_longest has a `fillvalue` keyword argument that allows you to specify a fill value other than `None`. – RufusVS Apr 04 '19 at 01:17
1
s = [1,2,3,4,5,6,7,8,9]
size = 4
chuck_list = [s[i:i+size] for i in range(0, len(s), size)]
  • 1
    New answers to old, well-answered questions should contain ample explanation on how they complement the other answers. – Gert Arnold Feb 19 '22 at 19:21
  • 1
    It helps more if you supply an explanation why this is the preferred solution and explain how it works. We want to educate, not just provide code. – the Tin Man Feb 21 '22 at 05:18
0

The source code of the grouper() function included in the question is copied from the documentation for itertools, more specifically the section Itertools Recipes.

In this documentation, it is stated that:

This section shows recipes for creating an extended toolset using the existing itertools as building blocks.

Substantially all of these recipes and many, many others can be installed from the more-itertools project found on the Python Package Index:

pip install more-itertools

If you look at the documentation of the more-itertools package, then you will notice that the objective pursued in the question, i.e. to chunk a list, could be achieved with the following functions present in this package:

  • sliced() to yield a list from sliceable iterables,
  • chunked() to yield a list from non-sliceable iterables.
  • ichunked() to yield iterables instead of lists.

For many readers who would land on this page, as they are looking for a solution which fulfills this objective, this packages offers several advantages: i) code is always up-to-date, ii) many other useful functions are present.

Wok
  • 4,956
  • 7
  • 42
  • 64