2

Possible Duplicate:
How do you split a list into evenly sized chunks in Python?

Hi,

I would like to split a list in many list of a length of x elements, like:

a = (1, 2, 3, 4, 5)

and get :

b = ( (1,2), (3,4), (5,) )

if the length is set to 2 or :

b = ( (1,2,3), (4,5) )

if the length is equal to 3 ...

Is there a nice way to write this ? Otherwise I think the best way is to write it using an iterator ...

Community
  • 1
  • 1
Fabien Engels
  • 363
  • 1
  • 5
  • 15
  • 2
    First of all, () is a tuple, not a list. In case you don't know, a tuple is essentially an immutable list. – Chris Morgan Nov 13 '10 at 01:00
  • 2
    Basically the same as [How do you split a list into evenly sized chunks in Python? ](http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python), except that you may have to add a `tuple` conversion. – Matthew Flaschen Nov 13 '10 at 01:00
  • Yeah, that question has a more comprehensive list (;-)) of answers including one identical to mine except in name. – Chris Morgan Nov 13 '10 at 01:24

3 Answers3

3

Here's how I'd do it. Iteration, but in a list comprehension. Note the type gets mixed; this may or may not be desired.

def sublist(seq, length):
    return [seq[i:i + length] for i in xrange(0, len(seq), length)]

Usage:

>>> sublist((1, 2, 3, 4, 5), 1)
[(1,), (2,), (3,), (4,), (5,)]

>>> sublist([1, 2, 3, 4, 5], 2)
[[1, 2], [3, 4], [5]]

>>> sublist('12345', 3)
['123', '45']

>>> sublist([1, 2, 3, 4, 5], 73)
[[1, 2, 3, 4, 5]]

>>> sublist((1, 2, 3, 4, 5), 0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in sublist
ValueError: xrange() arg 3 must not be zero

Of course, you can easily make it produce a tuple too if you want - replace the list comprehension [...] with tuple(...). You could also replace seq[i:i + length] with tuple(seq[i:i + length]) or list(seq[i:i + length]) to make it return a fixed type.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • My results with your code do not match: `>>> sublist((1, 2, 3, 4, 5), 1) [(1,), (2,), (3,), (4,), (5,)] >>> sublist((1, 2, 3, 4, 5), 2) [(1, 2), (3, 4), (5,)] ` They are correct, but use tuples, and your notation for tuples in the first case is wrong. Did you cut and paste these results from a shell session? – hughdbrown Nov 13 '10 at 02:02
  • @hughdbrown: Sorry, normally I do but for once I cheated, knowing what it would do, and made a mistake in the tuple notation. Naturally it should be (1,) etc. rather than (1). Corrected now. – Chris Morgan Nov 13 '10 at 02:07
2

The itertools module documentation. Read it, learn it, love it.

Specifically, from the recipes section:

import itertools

def grouper(n, iterable, fillvalue=None):
  "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
  args = [iter(iterable)] * n
  return itertools.izip_longest(fillvalue=fillvalue, *args)

Which gives:

>>> tuple(grouper(3, (1,2,3,4,5)))
((1, 2, 3), (4, 5, None))

which isn't quite what you want...you don't want the None in there...so. a quick fix:

>>> tuple(tuple(n for n in t if n) for t in grouper(3, (1,2,3,4,5)))
((1, 2, 3), (4, 5))

If you don't like typing the list comprehension every time, we can move its logic into the function:

def my_grouper(n, iterable):
  "my_grouper(3, 'ABCDEFG') --> ABC DEF G"
  args = [iter(iterable)] * n
  return tuple(tuple(n for n in t if n)
       for t in itertools.izip_longest(*args))

Which gives:

>>> tuple(my_grouper(3, (1,2,3,4,5)))
((1, 2, 3), (4, 5))

Done.

Peter Milley
  • 2,768
  • 19
  • 18
2

From the python docs on the itertools module:

from itertools import izip_longest
def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

Example:

>>> tuple(grouper(3, (1, 2, 3, 4, 5, 6, 7)))
((1, 2, 3), (4, 5, 6), (7, None, None))
UloPe
  • 645
  • 1
  • 7
  • 16