Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Referencing list entries within a for loop without indexes, possible?

A question of particular interest about python for loops. Engineering programs often require values at previous or future indexes, such as:

for i in range(0,n):
    value = 0.3*list[i-1] + 0.5*list[i] + 0.2*list[i+1]

etc...

However I rather like the nice clean python syntax:

for item in list:
    #Do stuff with item in list

or for a list of 2d point data:

for [x,y] in list:
    #Process x, y data

I like the concept of looping over a list without explicitly using an index to reference the items in the list. I was wondering if there was a clean way to grab the previous or next item without looping over the index (or without keeping track of the index independently)?

EDIT:

Thanks Andrew Jaffe (and by proxy Mark Byers) and gnibbler for the simple, extendable examples. I wasn't aware of the itertools or nwise modules till now. John Machin - thanks for the very complex example of what NOT to do. You put a lot of effort into this example, obviously the somewhat recursive algorithm I presented cannot produce a list with the same number of elements as the input list and it presents problems if not using explicit indexes. An algorithm like this would commonly occur in signal processing.

like image 906
user339860 Avatar asked Aug 17 '10 03:08

user339860


3 Answers

Here's a recipe, based on the itertools pairwise code, which does general n-wise grouping:

import itertools

def nwise(iterable, n=2):
    "s->(s_0,s_1, ..., s_n), (s_1,s_2,..., s_n+1), ... "
    ntup = itertools.tee(iterable, n)
    for i, item in enumerate(ntup):
        for ii in range(i):
            next(item, None)
    return itertools.izip(*ntup)

Which can be used thusly:

>>> import nwise
>>> ll = range(10)
>>> for tup in nwise.nwise(ll,3): print tup
... 
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, 5)
(4, 5, 6)
(5, 6, 7)
(6, 7, 8)
(7, 8, 9)

[Thanks to Mark Byers' answer for the idea]

like image 107
Andrew Jaffe Avatar answered Sep 21 '22 05:09

Andrew Jaffe


To have access to an element and the next one you can use the pairwise recipe that is shown in the itertools documentation:

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

This could be adapted to allow access to three neighbouring elements instead of just two.

like image 37
Mark Byers Avatar answered Sep 18 '22 05:09

Mark Byers


>>> from itertools import islice, izip
>>> seq = range(10)
>>> for x,y,z in izip(*(islice(seq,i,None) for i in range(3))):
...  print x,y,z
... 
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9

this can be trivially extended beyond 3 items.

If you need it to work with any iterable, Andrew's answer is suitable, or you can do it like this

>>> from itertools import izip, islice, tee
>>> seq=(x for x in range(10))
>>> for x,y,z in izip(*(islice(j,i,None) for i,j in enumerate(tee(seq,3)))):
...  print x,y,z
... 
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
like image 40
John La Rooy Avatar answered Sep 20 '22 05:09

John La Rooy