26

Is there any simple way to convert [1,2,3,4,5,6,7,8,9] to [[1,2,3],[4,5,6],[7,8,9]], without an explicit for loop?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Serg Melikyan
  • 369
  • 1
  • 5
  • 7

5 Answers5

67
>>> x = [1,2,3,4,5,6,7,8,9]
>>> zip(*[iter(x)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]

How does zip(*[iter(s)]*n) work in Python?

Community
  • 1
  • 1
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • 4
    Explain it a little bit too :P – Serdalis Apr 28 '12 at 14:30
  • 1
    It has been explained before but i will find a link. – jamylak Apr 28 '12 at 14:30
  • 3
    That is exceedingly clever, but I guess it is pythonic. – ddaa Apr 28 '12 at 14:54
  • 34
    Note that this discards incomplete chunks. If you try it with `x = [1,2,3,4,5,6,7]` then you only get two chunks, and the `7` is discarded. (Of course, this might be what you want, but if it isn't, beware!) – gimboland Jun 14 '13 at 00:00
  • @gimboland that input is invalid because there are no possible **even** chunks from that – jamylak Jun 14 '13 at 01:23
  • I do believe that the author of the question was asking about automatically determining the chunk size (so that all chunks are of equal size). This answer here along with all others use a predefined chunk size of 3. – rbaleksandar Jul 22 '15 at 10:09
  • @rbaleksandar There is no unique chunk size to determine. Each list could be split into equal chunks of `1` or `N` (number of items in list), or some factor of N. There is no 1 specific best number for this, if this is needed the number can be thought of based on personal preference – jamylak Jul 22 '15 at 15:46
  • Incorrect. I just manage to generate a QGridLayout with N buttons (N selected by the user) by splitting the list of buttons based purely on size of the list and the screen dimensions (width == height, width < height or width > height). This is what I mean by dynamically picking the chunk size -> it is not based on some static input by the user. – rbaleksandar Jul 22 '15 at 16:08
  • @rbaleksandar What are you talking about? The question is "How to split python list into chunks of equal size?". My answer splits into `3` equal sized chunks of `3`. Maybe there are 12 items and you need `4` chunks of `3` or `3` chunks of `4`. The user can come up with whatever number they want. You're talking about your `QGridLayout` now, submit your own answer which you think properly answers the question if you want me to see – jamylak Jul 22 '15 at 22:20
  • The question can be interpreted more generally and the give list there only as an example. The question itself is unclear if the author wants a specific solution for THAT list in particular or a general solution. The second makes more sense since the first can also be done just by looking at it and doing the splitting manually hence I believe a more general solutions should be given as an answer. The QGridLayout in my comment was just to illustrated a possible application for the general solution of the question. That's all. – rbaleksandar Jul 23 '15 at 00:08
  • @rbaleksandar You said i'm not answering the question properly, show me your answer so I can see. – jamylak Jul 23 '15 at 08:15
  • Is this really "pythonic" when it's entirely incomprehensible without a deep, complex explanation? It's pythonic only in that it uses python-only syntax, but it's operation really rather obfuscated - doesn't that go against the core Python value of "readability"? It certainly is interesting though! – Demis Nov 26 '15 at 07:45
  • Just put it into a function with a descriptive name and comment it if need be – jamylak Dec 01 '15 at 03:52
14

If you really want the sub elements to be lists vs tuples:

In [9]: [list(t) for t in zip(*[iter(range(1,10))]*3)]
Out[9]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Or, if you want to include the left over elements that would be truncated by zip, use a slice syntax:

In [16]: l=range(14)

In [17]: [l[i:i+3] for i in range(0,len(l),3)]
Out[17]: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13]]
the wolf
  • 34,510
  • 13
  • 53
  • 71
10

You can use numpy.reshape here as well:

import numpy as np

x = np.array([1,2,3,4,5,6,7,8,9])

new_x = np.reshape(x, (3,3))

Result:

>>> new_x
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
Akavall
  • 82,592
  • 51
  • 207
  • 251
8
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
jamylak
  • 128,818
  • 30
  • 231
  • 230
shiva
  • 2,674
  • 4
  • 23
  • 37
  • 1
    This just seems like a less readable version of my code... – jamylak Apr 28 '12 at 15:23
  • This exact code is provided in the accepted answer in the link in jamylak's post. – Akavall Apr 28 '12 at 15:28
  • I guess it's okay to know that it works but I wouldn't recommend using it because of what I said before. – jamylak Apr 28 '12 at 15:32
  • 5
    @jamylak, actually, this does someting slightly different from what your code does. Look at the result of `map(None, *[iter(range(10))]*3))` vs `zip(*[iter(range(10))]*3)`. Since the OP didn't specify which behavior he or she wants, this answer is valid. – senderle Apr 28 '12 at 15:35
  • 1
    @senderle for that i would use `izip_longest`. That is also used in the example for `itertools` – jamylak Apr 28 '12 at 15:36
  • jamylak's version loose the last tuple if it does not have exactly three elements while shiva's version use None to fil the last tuple but it puts all original elements in the answer. – alemol Apr 30 '15 at 16:15
0

Here's a much less "clever" way of doing it with recursion:

from itertools import chain

def groupsof(n, xs):
    if len(xs) < n:
        return [xs]
    else:
        return chain([xs[0:n]], groupsof(n, xs[n:]))

print list(groupsof(3, [1,2,3,4,5,6,7,8,9,10,11,12,13]))
Wes
  • 2,100
  • 1
  • 17
  • 31