# Pairs from single list

Posted on

### Question :

Pairs from single list

Often enough, I’ve found the need to process a list by pairs. I was wondering which would be the pythonic and efficient way to do it, and found this on Google:

``````pairs = zip(t[::2], t[1::2])
``````

I thought that was pythonic enough, but after a recent discussion involving idioms versus efficiency, I decided to do some tests:

``````import time
from itertools import islice, izip

def pairs_1(t):
return zip(t[::2], t[1::2])

def pairs_2(t):
return izip(t[::2], t[1::2])

def pairs_3(t):
return izip(islice(t,None,None,2), islice(t,1,None,2))

A = range(10000)
B = xrange(len(A))

def pairs_4(t):
# ignore value of t!
t = B
return izip(islice(t,None,None,2), islice(t,1,None,2))

for f in pairs_1, pairs_2, pairs_3, pairs_4:
# time the pairing
s = time.time()
for i in range(1000):
p = f(A)
t1 = time.time() - s

# time using the pairs
s = time.time()
for i in range(1000):
p = f(A)
for a, b in p:
pass
t2 = time.time() - s
print t1, t2, t2-t1
``````

These were the results on my computer:

``````1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578
``````

If I’m interpreting them correctly, that should mean that the implementation of lists, list indexing, and list slicing in Python is very efficient. It’s a result both comforting and unexpected.

Is there another, “better” way of traversing a list in pairs?

Note that if the list has an odd number of elements then the last one will not be in any of the pairs.

Which would be the right way to ensure that all elements are included?

``````def pairwise(t):
it = iter(t)
return izip(it, it)

def chunkwise(t, size=2):
it = iter(t)
return izip(*[it]*size)
``````

These are the results:

``````0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176
``````

## Results so far

Most pythonic and very efficient:

``````pairs = izip(t[::2], t[1::2])
``````

Most efficient and very pythonic:

``````pairs = izip(*[iter(t)]*2)
``````

It took me a moment to grok that the first answer uses two iterators while the second uses a single one.

To deal with sequences with an odd number of elements, the suggestion has been to augment the original sequence adding one element (`None`) that gets paired with the previous last element, something that can be achieved with `itertools.izip_longest()`.

## Finally

Note that, in Python 3.x, `zip()` behaves as `itertools.izip()`, and `itertools.izip()` is gone.

My favorite way to do it:

``````from itertools import izip

def pairwise(t):
it = iter(t)
return izip(it,it)

# for "pairs" of any length
def chunkwise(t, size=2):
it = iter(t)
return izip(*[it]*size)
``````

When you want to pair all elements you obviously might need a fillvalue:

``````from itertools import izip_longest
def blockwise(t, size=2, fillvalue=None):
it = iter(t)
return izip_longest(*[it]*size, fillvalue=fillvalue)
``````

I’d say that your initial solution `pairs = zip(t[::2], t[1::2])` is the best one because it is easiest to read (and in Python 3, `zip` automatically returns an iterator instead of a list).

To ensure that all elements are included, you could simply extend the list by `None`.

Then, if the list has an odd number of elements, the last pair will be `(item, None)`.

``````>>> t = [1,2,3,4,5]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, None)]
>>> t = [1,2,3,4,5,6]
>>> t.append(None)
>>> zip(t[::2], t[1::2])
[(1, 2), (3, 4), (5, 6)]
``````

I start with small disclaimer – don’t use the code below. It’s not Pythonic at all, I wrote just for fun. It’s similar to @THC4k `pairwise` function but it uses `iter` and `lambda` closures. It doesn’t use `itertools` module and doesn’t support `fillvalue`. I put it here because someone might find it interesting:

``````pairwise = lambda t: iter((lambda f: lambda: (f(), f()))(iter(t).next), None)
``````

As far as most pythonic goes, I’d say the recipes supplied in the python source docs (some of which look a lot like the answers that @JochenRitzel provided) is probably your best bet ðŸ˜‰

``````def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
``````

On modern python you just have to use `zip_longest(*args, fillvalue=fillvalue)` according to the corresponding doc page.

Is there another, “better” way of traversing a list in pairs?

I can’t say for sure but I doubt it: Any other traversal would include more Python code which has to be interpreted. The built-in functions like zip() are written in C which is much faster.

Which would be the right way to ensure that all elements are included?

Check the length of the list and if it’s odd (`len(list) & 1 == 1`), copy the list and append an item.

``````>>> my_list = [1,2,3,4,5,6,7,8,9,10]
>>> my_pairs = list()
>>> while(my_list):
...     a = my_list.pop(0); b = my_list.pop(0)
...     my_pairs.append((a,b))
...
>>> print(my_pairs)
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
``````

Only do it:

``````>>> l = [1, 2, 3, 4, 5, 6]
>>> [(x,y) for x,y in zip(l[:-1], l[1:])]
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
``````

Here is an example of creating pairs/legs by using a generator. Generators are free from stack limits

``````def pairwise(data):
zip(data[::2], data[1::2])
``````

Example:

``````print(list(pairwise(range(10))))
``````

Output:

``````[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
``````