Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Python combine two iterator based on time

I'd like to combine two iterators and yield the value(s) of the iterator that has the highest timestamp.

Minimal example and expectations:

# Outputs of these generators are "timestamps"
def gen_even():
    for x in range(0, 11, 2):
        yield x
        
def gen_odd():
    for x in sorted(list(range(1, 15, 2)) + [6]):
        yield x

Combining these two should result in in the following sequence [0, 1, 2, 3, 4, 5, (6, 6), 7, 8, 9, 10, 11, 13]

I tried the following which runs into StopIteration after gen1 has been consumed.

gen1 = gen_even()
gen2 = gen_odd()

def gen_both(gen1, gen2):
    first = next(gen1)
    second = next(gen2)
    
    while True:   
        if first < second:
            yield first
            first = next(gen1)
        elif first == second:  
            yield first, second
            first = next(gen1)
            second = next(gen2)            
        else:
            yield second
            second = next(gen2)


gen = gen_both(gen1, gen2)

for i in gen:
    print(i)

Output:

0
1
2
3
4
5
(6, 6)
7
8
9
10
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[8], line 11, in gen_both(gen1, gen2)
     10     yield first
---> 11     first = next(gen1)
     12 elif first == second:  

StopIteration: 

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
Cell In[8], line 21
     18             second = next(gen2)
     20 gen = gen_both(gen1, gen2)
---> 21 for i in gen:
     22     print(i)

RuntimeError: generator raised StopIteration

How can I do this in Python?

like image 606
fabsen Avatar asked Aug 31 '25 01:08

fabsen


1 Answers

You could avoid the stop iterator by handling it as follows:

def gen_both(gen1, gen2):
    first = next(gen1, None)
    second = next(gen2, None)
    
    while True:
        if first is None and second is None:
            break
        elif first is None:
            yield second
            second = next(gen2, None)
        elif second is None:
            yield first
            first = next(gen1, None)
        else:
            if first < second:
                yield first
                first = next(gen1, None)
            elif first == second:  
                yield first, second
                first = next(gen1, None)
                second = next(gen2, None)            
            else:
                yield second
                second = next(gen2, None)
like image 70
Marco F. Avatar answered Sep 02 '25 14:09

Marco F.