Best pattern/idiom

C

Chris Connett

I was wondering if there were a well known pattern/idiom for breaking a
sequence into a list of lists, each n elements long, e.g.

[0,1,2,3,4,5,6,7,8,9,10,11] -> [[0,1,2,3],[4,5,6,7],[8,9,10,11]]

This comes up reasonably often in my work, and every time I re-think
about it, and come up with
[ lis[n:n+4] for n in range( 0, len( lis ), 4 ) ]
which seems very kludgy to me, since it uses a range and len, 2 mentions
of the list identifier and 2 literal 4's (which is the size I want to
break into this time).

Is there a better way?
 
P

Peter Hansen

Chris said:
I was wondering if there were a well known pattern/idiom for breaking a
sequence into a list of lists, each n elements long, e.g.

[0,1,2,3,4,5,6,7,8,9,10,11] -> [[0,1,2,3],[4,5,6,7],[8,9,10,11]]

This comes up reasonably often in my work, and every time I re-think
about it, and come up with
[ lis[n:n+4] for n in range( 0, len( lis ), 4 ) ]
which seems very kludgy to me, since it uses a range and len, 2 mentions
of the list identifier and 2 literal 4's (which is the size I want to
break into this time).

Is there a better way?

Found from a Peter Otten post via Google Groups (searching for
"islice list split"):
.... for i in range(0, len(s), size):
.... yield s[i:i+size]
>>> s [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> list(chunks(s, 4)) [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]
>>> list(chunks([], 4)) []
>>> list(chunks(s[:10], 4))
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9]]

Of course, this uses range and len as well, and two mentions
of the identifier. A function would have let you avoid the
duplicated literal 4, as this does also.

Now, define "better". ;-) Some tasks have an inherent
complexity that can't be shrunk below a certain size...

-Peter
 
T

Tom B.

" [ lis[n:n+4] for n in range( 0, len( lis ), 4 ) ]
which seems very kludgy to me, since it uses a range and len, "

range and len are kludgy only to non-python programers.

Tom
 
M

Michele Simionato

Chris Connett said:
I was wondering if there were a well known pattern/idiom for breaking a
sequence into a list of lists, each n elements long, e.g.

[0,1,2,3,4,5,6,7,8,9,10,11] -> [[0,1,2,3],[4,5,6,7],[8,9,10,11]]

This comes up reasonably often in my work, and every time I re-think
about it, and come up with
[ lis[n:n+4] for n in range( 0, len( lis ), 4 ) ]
which seems very kludgy to me, since it uses a range and len, 2 mentions
of the list identifier and 2 literal 4's (which is the size I want to
break into this time).

Is there a better way?

From a post of mine of some time ago ...
.... tup = (iter(it),)*n
.... return itertools.izip(*tup)
....
list(chop([1,2,3,4,5,6],3)) [(1, 2, 3), (4, 5, 6)]
list(chop([1,2,3,4,5,6],2)) [(1, 2), (3, 4), (5, 6)]
list(chop([1,2,3,4,5,6],1))
[(1,), (2,), (3,), (4,), (5,), (6,)]

Michele Simionato
 
H

Heiko Wundram

Am Dienstag, 10. August 2004 06:39 schrieb Michele Simionato:
... tup = (iter(it),)*n
... return itertools.izip(*tup)
...
list(chop([1,2,3,4,5,6],3)) [(1, 2, 3), (4, 5, 6)]
list(chop([1,2,3,4,5,6],2)) [(1, 2), (3, 4), (5, 6)]
list(chop([1,2,3,4,5,6],1)) [(1,), (2,), (3,), (4,), (5,), (6,)]

Problem being:
[(1, 2, 3, 4)]

If you actually want to get back everything from iterator, better do something
like:

def ichop(it,n,rtype=list):
it = iter(it)
empty = False
while not empty:
retv = []
while not empty and len(retv) < n:
try:
retv.append(it.next())
except StopIteration:
empty = True
if retv:
yield rtype(retv)
list(ichop([1,2,3,4,5,6],3)) [[1, 2, 3], [4, 5, 6]]
list(ichop([1,2,3,4,5,6],2)) [[1, 2], [3, 4], [5, 6]]
list(ichop([1,2,3,4,5,6],1)) [[1], [2], [3], [4], [5], [6]]
list(ichop([1,2,3,4,5,6],4,tuple)) [(1, 2, 3, 4), (5, 6)]
list(ichop([1,2,3,4,5,6],4))
[[1, 2, 3, 4], [5, 6]]

Heiko.
 
D

Duncan Booth

Problem being:
list(chop([1,2,3,4,5,6],4))
[(1, 2, 3, 4)]

If you actually want to get back everything from iterator, better do
something like:

def ichop(it,n,rtype=list):
it = iter(it)
empty = False
while not empty:
retv = []
while not empty and len(retv) < n:
try:
retv.append(it.next())
except StopIteration:
empty = True
if retv:
yield rtype(retv)

It depends what you actually want to get if the list isn't a multiple of n.
The uneven length tuple at the end seems a bit bad, you are probably better
off with something like this:
tup = (itertools.chain(iter(it), (None,)*(n-1)),)*n
return itertools.izip(*tup)
[(1, 2, 3, 4), (5, 6, None, None)]

although this still loses your data if n < 1.
 
C

Chris Connett

Michele said:
Chris Connett said:
I was wondering if there were a well known pattern/idiom for breaking a
sequence into a list of lists, each n elements long, e.g.

[0,1,2,3,4,5,6,7,8,9,10,11] -> [[0,1,2,3],[4,5,6,7],[8,9,10,11]]

This comes up reasonably often in my work, and every time I re-think
about it, and come up with
[ lis[n:n+4] for n in range( 0, len( lis ), 4 ) ]
which seems very kludgy to me, since it uses a range and len, 2 mentions
of the list identifier and 2 literal 4's (which is the size I want to
break into this time).

Is there a better way?


From a post of mine of some time ago ...


... tup = (iter(it),)*n
... return itertools.izip(*tup)
...
list(chop([1,2,3,4,5,6],3)) [(1, 2, 3), (4, 5, 6)]
list(chop([1,2,3,4,5,6],2)) [(1, 2), (3, 4), (5, 6)]
list(chop([1,2,3,4,5,6],1))

[(1,), (2,), (3,), (4,), (5,), (6,)]

Michele Simionato

That's slick! :) Though, objectively, it might be less maintainable,
since if I didn't know what it was doing, it'd take me a minute to
figure it out. I'll definitely keep that idea in my toolbox though!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top