Iterate through list two items at a time

D

Dave Dean

Hi all,
I'm looking for a way to iterate through a list, two (or more) items at a
time. Basically...

myList = [1,2,3,4,5,6]

I'd like to be able to pull out two items at a time - simple examples would
be:
Create this output:
1 2
3 4
5 6

Create this list:
[(1,2), (3,4), (5,6)]

I want the following syntax to work, but sadly it does not:
for x,y in myList:
print x, y

I can do this with a simple foreach statement in tcl, and if it's easy in
tcl it's probably not too hard in Python.

Thanks,
Dave
 
D

Dan Bishop

Hi all,
I'm looking for a way to iterate through a list, two (or more) items at a
time. Basically...

myList = [1,2,3,4,5,6]

I'd like to be able to pull out two items at a time...

def pair_list(list_):
return [list_[i:i+2] for i in xrange(0, len(list_), 2)]
 
B

bearophileHUGS

Few alternative solutions (other are possible), I usually use a variant
of the first version, inside a partition function, the second variant
is shorter when you don't have a handy partition() function and you
don't want to import modules, and the forth one needs less memory when
the data is very long:

from itertools import izip, islice

data = [1,2,3,4,5,6,7]

for x1, x2 in (data[i:i+2] for i in xrange(0, len(data)/2*2, 2)):
print x1, x2

for x1, x2 in zip(data[::2], data[1::2]):
print x1, x2

for x1, x2 in izip(data[::2], data[1::2]):
print x1, x2

for x1, x2 in izip(islice(data,0,None,2), islice(data,1,None,2)):
print x1, x2

Bye,
bearophile
 
S

skip

>> I'm looking for a way to iterate through a list, two (or more) items
>> at a time. Basically...
>>
>> myList = [1,2,3,4,5,6]
>>
>> I'd like to be able to pull out two items at a time...

Dan> def pair_list(list_):
Dan> return [list_[i:i+2] for i in xrange(0, len(list_), 2)]

Here's another way (seems a bit clearer to me, but each person has their own
way of seeing things):
>>> import string
>>> string.letters 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> zip(string.letters[::2], string.letters[1::2])
[('a', 'b'), ('c', 'd'), ..., ('W', 'X'), ('Y', 'Z')]

It extends readily to longer groupings:
>>> zip(string.letters[::3], string.letters[1::3], string.letters[2::3])
[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'h', 'i'), ...

Obviously, if your lists are long, you can substitute itertools.izip for
zip. There's probably some easy way to achieve the same result with
itertools.groupby, but I'm out of my experience there...

Skip
 
G

Gabriel Genellina

myList = [1,2,3,4,5,6]

I'd like to be able to pull out two items at a time - simple examples would
be:
Create this output:
1 2
3 4
5 6

b=iter(a)
for x in b:
y=b.next()
print x,y

b=iter(a)
for x,y in ((item, b.next()) for item in b):
print x,y
Create this list:
[(1,2), (3,4), (5,6)]

b=iter(a)
[(item, b.next()) for item in b]

Note that they don't behave the same at the corner cases (empty list,
single item, odd length...)


--
Gabriel Genellina
Softlab SRL






__________________________________________________
Preguntá. Respondé. Descubrí.
Todo lo que querías saber, y lo que ni imaginabas,
está en Yahoo! Respuestas (Beta).
¡Probalo ya!
http://www.yahoo.com.ar/respuestas
 
J

Jeffrey Froman

Dave said:
I'm looking for a way to iterate through a list, two (or more) items at a
time.

Here's a solution, from the iterools documentation. It may not be the /most/
beautiful, but it is short, and scales well for larger groupings:
.... return izip(* [iter(iterable)] * n)
....
list(groupn(myList, 2)) [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11)]
list(groupn(myList, 3)) [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)]
list(groupn(myList, 4)) [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
for a,b in groupn(myList, 2):
.... print a, b
....
0 1
2 3
4 5
6 7
8 9
10 11
Jeffrey
 
D

Dave Dean

Thanks for all the fast responses. I'm particularly a fan of the zip
method, followed closely by the xrange example. All, of course, are a lot
of help!
Thanks,
Dave
 
G

Gerard Flanagan

Dave said:
Hi all,
I'm looking for a way to iterate through a list, two (or more) items at a
time. Basically...

myList = [1,2,3,4,5,6]

I'd like to be able to pull out two items at a time - simple examples would
be:
Create this output:
1 2
3 4
5 6

Create this list:
[(1,2), (3,4), (5,6)]

A "padding generator" version:

def chunk( seq, size, pad=None ):
'''
Slice a list into consecutive disjoint 'chunks' of
length equal to size. The last chunk is padded if necessary.
>>> list(chunk(range(1,10),3)) [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> list(chunk(range(1,9),3)) [[1, 2, 3], [4, 5, 6], [7, 8, None]]
>>> list(chunk(range(1,8),3)) [[1, 2, 3], [4, 5, 6], [7, None, None]]
>>> list(chunk(range(1,10),1)) [[1], [2], [3], [4], [5], [6], [7], [8], [9]]
>>> list(chunk(range(1,10),9)) [[1, 2, 3, 4, 5, 6, 7, 8, 9]]
>>> for X in chunk([],3): print X
>>>
'''
n = len(seq)
mod = n % size
for i in xrange(0, n-mod, size):
yield seq[i:i+size]
if mod:
padding = [pad] * (size-mod)
yield seq[-mod:] + padding
 
J

J. Clifford Dyer

Gabriel said:
b=iter(a)
for x in b:
y=b.next()
print x,y

So as not to choke on odd-length lists, you could try

a = [1,2,3,4,5,6,7]
b = iter(a)
for x in b:
try:
y=b.next()
except StopIteration:
y=None
print x,y

Substitute in whatever for y=None that you like.

Cheers,
Cliff
 
W

Wade Leftwich

Jeffrey said:
Dave said:
I'm looking for a way to iterate through a list, two (or more) items at a
time.

Here's a solution, from the iterools documentation. It may not be the /most/
beautiful, but it is short, and scales well for larger groupings:
... return izip(* [iter(iterable)] * n)
...
list(groupn(myList, 2)) [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11)]
list(groupn(myList, 3)) [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)]
list(groupn(myList, 4)) [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
for a,b in groupn(myList, 2):
... print a, b
...
0 1
2 3
4 5
6 7
8 9
10 11
Jeffrey

This works great except you lose any 'remainder' from myList:
[(0, 1, 2), (3, 4, 5), (6, 7, 8)] # did not include (9,)

The following might be more complex than necessary but it solves the
problem, and like groupn()
it works on infinite lists.

from itertools import groupby, imap
def chunk(it, n=0):
if n == 0:
return iter([it])
grouped = groupby(enumerate(it), lambda x: int(x[0]/n))
counted = imap(lambda x:x[1], grouped)
return imap(lambda x: imap(lambda y: y[1], x), counted)
[list(x) for x in chunk(range(10), 3)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Note the chunks are iterators, not tuples as in groupn():
[x for x in chunk(range(10), 3)]
[<itertools.imap object at 0xb78d4c4c>,
<itertools.imap object at 0xb78d806c>,
<itertools.imap object at 0xb78d808c>,
<itertools.imap object at 0xb78d4c6c>]


-- Wade Leftwich
Ithaca, NY
 
W

Wade Leftwich

Wade said:
Jeffrey said:
Dave said:
I'm looking for a way to iterate through a list, two (or more) items at a
time.

Here's a solution, from the iterools documentation. It may not be the /most/
beautiful, but it is short, and scales well for larger groupings:
from itertools import izip
def groupn(iterable, n):
... return izip(* [iter(iterable)] * n)
...
list(groupn(myList, 2))
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11)]
list(groupn(myList, 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11)]
list(groupn(myList, 4))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
for a,b in groupn(myList, 2):
... print a, b
...
0 1
2 3
4 5
6 7
8 9
10 11
Jeffrey

This works great except you lose any 'remainder' from myList:
[(0, 1, 2), (3, 4, 5), (6, 7, 8)] # did not include (9,)

The following might be more complex than necessary but it solves the
problem, and like groupn()
it works on infinite lists.

from itertools import groupby, imap
def chunk(it, n=0):
if n == 0:
return iter([it])
grouped = groupby(enumerate(it), lambda x: int(x[0]/n))
counted = imap(lambda x:x[1], grouped)
return imap(lambda x: imap(lambda y: y[1], x), counted)
[list(x) for x in chunk(range(10), 3)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

Note the chunks are iterators, not tuples as in groupn():
[x for x in chunk(range(10), 3)]
[<itertools.imap object at 0xb78d4c4c>,
<itertools.imap object at 0xb78d806c>,
<itertools.imap object at 0xb78d808c>,
<itertools.imap object at 0xb78d4c6c>]


-- Wade Leftwich
Ithaca, NY

Or, using generator expressions instead of imap and getting rid of the
lambdas --

from itertools import groupby

def chunk(it, n=0):
if n == 0:
return iter([it])
def groupfun((x,y)):
return int(x/n)
grouped = groupby(enumerate(it), groupfun)
counted = (y for (x,y) in grouped)
return ((z for (y,z) in x) for x in counted)
[list(x) for x in chunk(range(10), 3)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
[x for x in chunk(range(10), 3)]
[<generator object at 0xb7a34e4c>,
<generator object at 0xb7a34dac>,
<generator object at 0xb7a34d2c>,
<generator object at 0xb7a34d6c>]
 
P

Peter Otten

Wade said:
from itertools import groupby

def chunk(it, n=0):
if n == 0:
return iter([it])
def groupfun((x,y)):
return int(x/n)
grouped = groupby(enumerate(it), groupfun)
counted = (y for (x,y) in grouped)
return ((z for (y,z) in x) for x in counted)
[list(x) for x in chunk(range(10), 3)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
[x for x in chunk(range(10), 3)]
[<generator object at 0xb7a34e4c>,
<generator object at 0xb7a34dac>,
<generator object at 0xb7a34d2c>,
<generator object at 0xb7a34d6c>]

Note that all but the last of these generators are useless:
chunks = [x for x in chunk(range(10), 3)]
[list(x) for x in chunks]
[[], [], [], [9]] # did you expect that?

Peter
 
W

Wade Leftwich

Peter said:
Wade said:
from itertools import groupby

def chunk(it, n=0):
if n == 0:
return iter([it])
def groupfun((x,y)):
return int(x/n)
grouped = groupby(enumerate(it), groupfun)
counted = (y for (x,y) in grouped)
return ((z for (y,z) in x) for x in counted)
[list(x) for x in chunk(range(10), 3)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
[x for x in chunk(range(10), 3)]
[<generator object at 0xb7a34e4c>,
<generator object at 0xb7a34dac>,
<generator object at 0xb7a34d2c>,
<generator object at 0xb7a34d6c>]

Note that all but the last of these generators are useless:
chunks = [x for x in chunk(range(10), 3)]
[list(x) for x in chunks]
[[], [], [], [9]] # did you expect that?
In [48]: chunkgen = chunk(range(10), 3)

In [49]: for x in chunkgen:
....: print list(x)
....:
....:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9]

That's an interesting gotcha that I've never run into when using this
function, because in practice I never put the generator returned by
chunk() inside a list comprehension.

In [51]: chunkgen = chunk(range(10), 3)

In [52]: [list(x) for x in chunkgen]
Out[52]: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

But, as you pointed out --

In [57]: chunkgen = chunk(range(10), 3)

In [58]: chunks = list(chunkgen)

In [59]: [list(x) for x in chunks]
Out[59]: [[], [], [], [9]]

So apparently when we list(chunkgen), we are exhausting the generators
that are its members. And if we do it again, we get rid of the [9] --

In [60]: [list(x) for x in chunks]
Out[60]: [[], [], [], []]

I'll admit that chunk() is needlessly tricky for most purposes. I wrote
it to accept a really lengthy, possibly unbounded, iterator and write
out 10,000-line files from it, and also to play with Python's new
functional capabilities.

-- Wade
 

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

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top