308

How do I make a for loop or a list comprehension so that every iteration gives me two elements?

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

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

Output:

1+2=3
3+4=7
5+6=11
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jackhab
  • 17,128
  • 37
  • 99
  • 136
  • 6
    For overlapping pair: https://stackoverflow.com/questions/5434891/iterate-a-list-as-pair-current-next-in-python – user202729 Aug 16 '19 at 12:09
  • 1
    Next time avoid to name something just 'l'. It can be quickly mixed up with some 1 or I or | Name it List ... or L (if ya wanna save space *facepalm*). – Nadu Nov 21 '20 at 13:54
  • 1
    Simply use a loop with two variables: for i,k in zip(range(1,7)[0::2], range(1,7)[1::2]): print str(i), '+', str(k), '=', str(i+k) – Shrm May 20 '21 at 16:18
  • As I've mentioned under the selected answer by @johnysweb below, the walrus operator can now be used (3.8+) to do this in a concise way: `for i, k in zip(_x := iter(mylist), _x): ...` – Alex Just Alex Jan 10 '23 at 20:14

22 Answers22

333

You need a pairwise() (or grouped()) implementation.

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return zip(a, a)

for x, y in pairwise(l):
   print("%d + %d = %d" % (x, y, x + y))

Or, more generally:

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return zip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print("%d + %d = %d" % (x, y, x + y))

In Python 2, you should import izip as a replacement for Python 3's built-in zip() function.

All credit to martineau for his answer to my question, I have found this to be very efficient as it only iterates once over the list and does not create any unnecessary lists in the process.

N.B: This should not be confused with the pairwise recipe in Python's own itertools documentation, which yields s -> (s0, s1), (s1, s2), (s2, s3), ..., as pointed out by @lazyr in the comments.

Little addition for those who would like to do type checking with mypy on Python 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • 31
    Not to be confused with the pairwise function suggested in the [itertools](http://docs.python.org/library/itertools.html) recipes section, which yields `s -> (s0,s1), (s1,s2), (s2, s3), ...` – Lauritz V. Thaulow Mar 22 '11 at 10:13
  • 1
    It does a different thing. Your version only yields half the number of pairs compared to the `itertools` recipe function with the same name. Of course yours is faster... – Sven Marnach Mar 22 '11 at 10:22
  • Huh? Your function and the function I referred to do different things, and that was the point of my comment. – Lauritz V. Thaulow Mar 22 '11 at 10:24
  • @lazyr, @Sven: Oh, I *see*. Humble apologies. I should put my glasses on! – johnsyweb Mar 22 '11 at 10:30
  • @Sven Marnach: I suspect the speed difference is due more to `itertools'` `pairwise()` being implemented in terms of `tee()` than because it returns less data for any given iterable -- of course it's also performing a somewhat different function altogether, so comparing them at all is questionable... – martineau Jul 23 '12 at 17:22
  • 11
    BE CAREFUL! Using these functions puts you at risk of not iterating over the last elements of an iterable. Example: list(grouped([1,2,3],2)) >>> [(1, 2)] .. when you'd expect [(1,2),(3,)] – egafni Jan 20 '13 at 18:48
  • 5
    @Erik49: In the case specified in the question, it wouldn't make sense to have an 'incomplete' tuple. If you wanted to include an incomplete tuple, you could use `izip_longest()` instead of `izip()`. E.g: `list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))` --> `[(1, 2), (3, 0)]`. Hope this helps. – johnsyweb Jan 21 '13 at 02:19
  • 1
    But **to** be confused with the `grouper` recipe in the same documentation. It's definitely worth understanding how this works—that's how you can decide what to do with jagged groups (skip the leftover values, fill with a fillvalue, or return a short group). – abarnert Aug 03 '14 at 12:18
  • @egafni do you how to fix your case? – Sailesh Kotha Jul 26 '18 at 04:04
  • @sailesh: You could use [`izip_longest()`](https://stackoverflow.com/questions/5389507/iterating-over-every-two-elements-in-a-list/5389547#comment20091344_5389547). – johnsyweb Jul 27 '18 at 00:51
  • 1
    @Johnsyweb Thanks, but my use case is pretty small. So, I'm using `current, items = items[:25], items[25:]` in a while loop which checks for `len(items)` – Sailesh Kotha Jul 27 '18 at 15:24
  • Thanks, mate. Your solution worked fine to me and helped me to solve a problem I've spent quite a time on it. – Necas Apr 08 '21 at 23:17
  • With Python 3.8+ this can now be done in a more concise way by using the walrus operator: `unpack2 = lambda lst: zip(_x := iter(lst), _x); for a, b in unpack2(l)` (still need to define a function), or, without the lambda: `for a, b in zip(_l := iter(l), _l): ...` Add more `_l` to unpack 3 elements at a time or more. – Alex Just Alex Jan 10 '23 at 20:06
274

Well you need tuple of 2 elements, so

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Where:

  • data[0::2] means create subset collection of elements that (index % 2 == 0)
  • zip(x,y) creates a tuple collection from x and y collections same index elements.
Margus
  • 19,694
  • 14
  • 55
  • 103
  • 9
    This can also be extended in case more than two elements are required. For e.g. `for i, j, k in zip(data[0::3], data[1::3], data[2::3]):` – lifebalance Jan 26 '14 at 15:53
  • 27
    So much cleaner than pulling in an import and defining a function! – kmarsh May 13 '14 at 20:19
  • 20
    @kmarsh: But this only works on sequences, the function works on any iterable; and this uses O(N) extra space, the function doesn't; on the other hand, this is generally faster. There are good reasons to pick one or the other; being afraid of `import` is not one of them. – abarnert Aug 03 '14 at 12:20
  • 2
    @abarnert `itertools.islice` to the rescue: `for i,k in zip(islice(data, 0, None, 2), islice(data, 1, None, 2):`. And, if you're worrying about "not iterating over the last elements of an iterable", replace `zip` with `itertools.zip_longest` and use a `fillvalue` that makes sense to you. – Escape0707 Sep 14 '20 at 04:50
  • I needed to get `s -> (s0, s1), (s1, s2), (s2, s3), ...` and I got it using this > `for i,k in zip(data[0::1], data[1::1]):` – Kapil Marwaha Dec 13 '20 at 07:40
  • Will this create two new lists? – Enzo Dtz Aug 01 '23 at 22:35
99
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
pyanon
  • 1,065
  • 6
  • 3
  • 11
    @HamidRohani `zip` returns a `zip` object in Python 3, which is not subscriptable. It needs to be converted to a sequence (`list`, `tuple`, etc.) first, but *"not working"* is a bit of a stretch. – vaultah Feb 25 '17 at 14:03
  • Just convert the `zip` object to a list of tuples like: `output = list(zip_object)` – Rob Irwin Feb 24 '23 at 19:53
78

A simple solution.

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

for i in range(0, len(l), 2):
    print str(l[i]), '+', str(l[i + 1]), '=', str(l[i] + l[i + 1])
taskinoor
  • 45,586
  • 12
  • 116
  • 142
59

While all the answers using zip are correct, I find that implementing the functionality yourself leads to more readable code:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

The it = iter(it) part ensures that it is actually an iterator, not just an iterable. If it already is an iterator, this line is a no-op.

Usage:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
mic_e
  • 5,594
  • 4
  • 34
  • 49
  • 2
    This solution allows to generalization to size of tuples > 2 – guilloptero May 28 '15 at 08:27
  • 1
    This solution also works if `it` is only an iterator and not an iterable. The other solutions seem to rely on the possibility to create two independent iterators for the sequence. – skyking Sep 04 '15 at 09:32
  • I found this approach at https://stackoverflow.com/a/16815056/2480481 before see this answer. Is cleaner, easier than dealing with zip(). – m3nda Jun 22 '17 at 18:34
  • 3
    I like that it allows to avoid tripling memory usage as the accepted answer. – Kentzo Aug 13 '18 at 23:15
  • This doesn't work nicely with `for` loops in Python 3.5+ due to [PEP 479](https://www.python.org/dev/peps/pep-0479/), which replaces any `StopIteration` raised in a generator with a `RuntimeError`. – sidney Nov 21 '19 at 12:18
  • @sidney thanks for the hint, but actually this only happens in Python 3.7+. Adjusting the reply accordingly. – mic_e Apr 17 '20 at 09:18
  • Similar approach generalized and works in all python version. https://stackoverflow.com/a/63923008 – balki Sep 16 '20 at 15:13
43

I hope this will be even more elegant way of doing it.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]
Georgy
  • 12,464
  • 7
  • 65
  • 73
Vivek Srinivasan
  • 2,687
  • 3
  • 17
  • 17
  • 2
    take care with lists with an odd length! Will omit last element – lode Nov 21 '20 at 06:43
  • Beautiful, I could not agree with you more. short sweet and elegant. I do have some questions since I am sort of new to some of python's libraries. Zip is one of them. first on a[::2] - if I understand correctly this will add 2 spaces for every iteration starting with the first value in the list. 1,3,5,etc. Now, on a[1::2] - – Jiraheta Jul 02 '21 at 19:54
  • Now, on a[1::2] - this will add +1 from the first value 1+1 = 2. Then add +2 for all the other iterations. Is this correct or am I missing something? – Jiraheta Jul 03 '21 at 01:26
  • 2
    For the odd length use `from itertools import zip_longest`. It will return `[(1, 2), (3, 4), (5, 6), (7, None)]` – egvo Aug 17 '21 at 08:42
33

In case you're interested in the performance, I did a small benchmark (using my library simple_benchmark) to compare the performance of the solutions and I included a function from one of my packages: iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

enter image description here

So if you want the fastest solution without external dependencies you probably should just use the approach given by Johnysweb (at the time of writing it's the most upvoted and accepted answer).

If you don't mind the additional dependency then the grouper from iteration_utilities will probably be a bit faster.

Additional thoughts

Some of the approaches have some restrictions, that haven't been discussed here.

For example a few solutions only work for sequences (that is lists, strings, etc.), for example Margus/pyanon/taskinoor solutions which uses indexing while other solutions work on any iterable (that is sequences and generators, iterators) like Johnysweb/mic_e/my solutions.

Then Johnysweb also provided a solution that works for other sizes than 2 while the other answers don't (okay, the iteration_utilities.grouper also allows setting the number of elements to "group").

Then there is also the question about what should happen if there is an odd number of elements in the list. Should the remaining item be dismissed? Should the list be padded to make it even sized? Should the remaining item be returned as single? The other answer don't address this point directly, however if I haven't overlooked anything they all follow the approach that the remaining item should be dismissed (except for taskinoors answer - that will actually raise an Exception).

With grouper you can decide what you want to do:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]
MSeifert
  • 145,886
  • 38
  • 333
  • 352
19

Use the zip and iter commands together:

I find this solution using iter to be quite elegant:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Which I found in the Python 3 zip documentation.

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

To generalise to N elements at a time:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
cs95
  • 379,657
  • 97
  • 704
  • 746
Quantum Mechanic
  • 625
  • 1
  • 6
  • 20
11
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) returns a tuple with the next element of each iterable.

l[::2] returns the 1st, the 3rd, the 5th, etc. element of the list: the first colon indicates that the slice starts at the beginning because there's no number behind it, the second colon is only needed if you want a 'step in the slice' (in this case 2).

l[1::2] does the same thing but starts in the second element of the lists so it returns the 2nd, the 4th, 6th, etc. element of the original list.

alexpinho98
  • 909
  • 8
  • 14
  • 4
    This answer was already given by Margus two years ago. http://stackoverflow.com/questions/5389507/iterating-over-every-two-elements-in-a-list#answer-5389578 – cababunga Aug 09 '13 at 01:04
  • 1
    1 for explaining how `[number::number]` syntax works. helpful for who doesn't use python often – Alby Dec 26 '13 at 21:33
9

With unpacking:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(f'{i}+{k}={i+k}') 

Note: this will consume l, leaving it empty afterward.

toaruScar
  • 401
  • 4
  • 6
8

There are many ways to do that. For example:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

list(zip(*[iter(lst)]*2))
>>>[(1, 2), (3, 4), (5, 6)]
Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36
Binit Bhagat
  • 181
  • 1
  • 5
4

you can use more_itertools package.

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')
Scott Ming
  • 199
  • 1
  • 6
2

For anyone it might help, here is a solution to a similar problem but with overlapping pairs (instead of mutually exclusive pairs).

From the Python itertools documentation:

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Or, more generally:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)
Chris Malek
  • 103
  • 9
2

The title of this question is misleading, you seem to be looking for consecutive pairs, but if you want to iterate over the set of all possible pairs than this will work :

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:
Ofek Ron
  • 8,354
  • 13
  • 55
  • 103
2

A simplistic approach:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

this is useful if your array is a and you want to iterate on it by pairs. To iterate on triplets or more just change the "range" step command, for example:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(you have to deal with excess values if your array length and the step do not fit)

mchrgr2000
  • 61
  • 4
2

Another try at cleaner solution

def grouped(itr, n=2):
    itr = iter(itr)
    end = object()
    while True:
        vals = tuple(next(itr, end) for _ in range(n))
        if vals[-1] is end:
            return
        yield vals

For more customization options

from collections.abc import Sized

def grouped(itr, n=2, /, truncate=True, fillvalue=None, strict=False, nofill=False):
    if strict:
        if isinstance(itr, Sized):
            if len(itr) % n != 0:
                raise ValueError(f"{len(itr)=} is not divisible by {n=}")
    itr = iter(itr)
    end = object()
    while True:
        vals = tuple(next(itr, end) for _ in range(n))
        if vals[-1] is end:
            if vals[0] is end:
                return
            if strict:
                raise ValueError("found extra stuff in iterable")
            if nofill:
                yield tuple(v for v in vals if v is not end)
                return
            if truncate:
                return
            yield tuple(v if v is not end else fillvalue for v in vals)
            return
        yield vals
balki
  • 26,394
  • 30
  • 105
  • 151
2

The polished Python3 solution is given in one of the itertools recipes:

import itertools

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 itertools.zip_longest(*args, fillvalue=fillvalue)
Hugues
  • 2,865
  • 1
  • 27
  • 39
1

Thought that this is a good place to share my generalization of this for n>2, which is just a sliding window over an iterable:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)
Yuval
  • 3,207
  • 32
  • 45
1

I need to divide a list by a number and fixed like this.

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

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]
Ali Katkar
  • 517
  • 4
  • 7
1

Using typing so you can verify data using mypy static analysis tool:

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
-1

Here we can have alt_elem method which can fit in your for loop.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Output:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Note: Above solution might not be efficient considering operations performed in func.

Sanket Sudake
  • 763
  • 6
  • 18
-2

This is simple solution, which is using range function to pick alternative elements from a list of elements.

Note: This is only valid for an even numbered list.

a_list = [1, 2, 3, 4, 5, 6]
empty_list = [] 
for i in range(0, len(a_list), 2):
    empty_list.append(a_list[i] + a_list[i + 1])   
print(empty_list)
# [3, 7, 11]
user391
  • 105
  • 1
  • 12
  • 2
    Try providing an explanation rather than just code. – lwisi Dec 03 '20 at 20:12
  • 1
    @LuisArgüelles's suggestion is especially critical here, where this question is a decade old and already has twenty answers. Imagine trying to sort through those answers to figure out which one is relevant? Try explaining what sets your answer apart and when your approach might be preferred. Are you relying on new syntax not addressed in the other answers? Is your approach faster or more efficient? Is it better suited for particular types of data (e.g., smaller arrays)? – Jeremy Caney Dec 03 '20 at 23:51