Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Elegant way of dual/multiple iteration over the same list

I've written a bit of code like the following to compare items with other items further on in a list. Is there a more elegant pattern for this sort of dual iteration?

jump_item_iter = (j for j in items if some_cond)
try:
    jump_item = jump_item_iter.next()
except StopIteration:
    return
for item in items:
    if jump_item is item:
        try:
            jump_item = jump_iter.next()
        except StopIteration:
            return
    # do lots of stuff with item and jump_item

I don't think the "except StopIteration" is very elegant

Edit:

To hopefully make it clearer, I want to visit each item in a list and pair it with the next item further on in the list (jump_item) which satisfies some_cond.

like image 251
EoghanM Avatar asked May 15 '09 10:05

EoghanM


4 Answers

As far as I can see any of the existing solutions work on a general one shot, possiboly infinite iterator, all of them seem to require an iterable.

Heres a solution to that.

def batch_by(condition, seq):
    it = iter(seq)
    batch = [it.next()]
    for jump_item in it:
        if condition(jump_item):
            for item in batch:
                yield item, jump_item
            batch = []
        batch.append(jump_item)

This will easily work on infinite iterators:

from itertools import count, islice
is_prime = lambda n: n == 2 or all(n % div for div in xrange(2,n))
print list(islice(batch_by(is_prime, count()), 100))

This will print first 100 integers with the prime number that follows them.

like image 61
Ants Aasma Avatar answered Oct 26 '22 13:10

Ants Aasma


I have no idea what compare() is doing, but 80% of the time, you can do this with a trivial dictionary or pair of dictionaries. Jumping around in a list is a kind of linear search. Linear Search -- to the extent possible -- should always be replaced with either a direct reference (i.e., a dict) or a tree search (using the bisect module).

like image 44
S.Lott Avatar answered Oct 26 '22 12:10

S.Lott


The following iterator is time and memory-efficient:

def jump_items(items):
    number_to_be_returned = 0
    for elmt in items:
        if <condition(elmt)>:
            for i in range(number_to_be_returned):
                yield elmt
            number_to_be_returned = 1
        else:
            number_to_be_returned += 1

for (item, jump_item) in zip(items, jump_items(items)):
    # do lots of stuff

Note that you may actually want to set the first number_to_be_returned to 1...

like image 1
Eric O Lebigot Avatar answered Oct 26 '22 14:10

Eric O Lebigot


Write a generator function:

def myIterator(someValue):
    yield (someValue[0], someValue[1])

for element1, element2 in myIterator(array):
     # do something with those elements.
like image 1
Georg Schölly Avatar answered Oct 26 '22 13:10

Georg Schölly