Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deal with StopIteration exception when iterating on two lists of different size

I want to iterate through two lists of different size, but not at the same pace (I only found answers for parallel iteration). For instance, consider two sorted lists that contain the same kind of elements and I want to do something on the elements according to the fact they are in only one list or in both lists, using a common traversal of the two lists.

I would know how to do that using indices, but is there a way to do that properly with iterators ?

I have something along the lines of:

list1=[...]
list2=[...]

it1=iter(list1)
it2=iter(list2)
try:
    while True:
        e1=it1.next()
        e2=it2.next()

        while compare(e1,e2):
            doSomething1(e1)
            e1=it1.next()

        while compare(e2,e1):
            doSomething2(e2)
            e2=it2.next()

        doSomething3(e1,e2)
except StopIteration:
    pass

# do something with the rest of the list that is at end

When the StopIteration is raised, I do not know which list is at end and I don't know how to access the remaining elements: calling next() to check for exception will make me skip an element in the longer list, accessing directly e1 or e2 will make me access the last element of the shorter list a second time.

I can imagine several workarounds, like having the try/except around each next() but all my ideas seems more complex than just using indices...

For a simple working example of the code above, you can use

def compare(e1,e2):      return e1<e2
def doSomething1(e1):    print "list1: %d"%e1
def doSomething2(e1):    print "list2: %d"%e1
def doSomething3(e1,e2): print "common: %d"%e1

list1=[1,2,5,6,8,10,23,56]
list2=[3,4,5,10]
like image 328
romaric Avatar asked Oct 14 '25 15:10

romaric


1 Answers

Iterators have a __length_hint__ which will return the (estimated) number of remaining elements to iterate over. The accuracy of this depends on the type of your iterator, and I can't guarantee it would work on non-CPython implementations.

x = [1,2,3,4,5]
y = iter(x)
print y.__length_hint__() # prints 5
print y.next # prints 1
...

In your StopIteration catch, you can check which iterator has remaining members and act based on that.

list1=[...]
list2=[...]

it1=iter(list1)
it2=iter(list2)
try:
    while True:
        e1=it1.next()
        e2=it2.next()

        while compare(e1,e2):
            doSomething1(e1)
            e1=it1.next()

        while compare(e2,e1):
            doSomething2(e2)
            e2=it2.next()

        doSomething3(e1,e2)
except StopIteration:
    if it1.__length_hint__() > 0:
        #handle unhandled list1 items.
    elif it2.__length_hint__() > 0:
        #handle unhandled list2 items.
    else:
        #both lists have been exhausted.
like image 186
Matt O Avatar answered Oct 17 '25 03:10

Matt O



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!