Mateusz Sadowski
Mateusz Sadowski

Reputation: 47

Why does this list comprehension based on zip object does not work in subsequent loop iterations?

I have a piece of code where I want to filter out a part of a list in consecutive loop iterations:

def calculate_delays(flash_time_stamps, sample_time_stamps, sample_values, threshold):
    delays = []
    first_thresh_cross_time_stamps = []
    samples = zip(sample_time_stamps, sample_values)
    # For each flash find the first sample that crosses the chosen threshold 
    # and calculate the difference between the corresponding timestamps
    for flash_time_stamp in flash_time_stamps:
        first_cross_thresh_time_stamp = -1
        # Ignore samples that occured before the flash
        samples_filtered = [s for s in samples if s[0] >= flash_time_stamp]    # ---COMPREHENSION IS HERE---
        for sample in samples_filtered:
            if sample[1] < threshold:
                first_cross_thresh_time_stamp = sample[0]
                break

        # Save
        first_thresh_cross_time_stamps.append(first_cross_thresh_time_stamp)
        delays.append(first_cross_thresh_time_stamp - flash_time_stamp)
    
    return first_thresh_cross_time_stamps, delays

In the first iteration, the code works as expected, but in the subsequent iterations, the list comprehension returns an empty list. I know that this should not be the case based on the data I'm passing. Also, the following code works as expected:

def calculate_delays(flash_time_stamps, sample_time_stamps, sample_values, threshold):
    delays = []
    first_thresh_cross_time_stamps = []
    samples = zip(sample_time_stamps, sample_values)
    # For each flash find the first sample that crosses the chosen threshold 
    # and calculate the difference between the corresponding timestamps
    for flash_time_stamp in flash_time_stamps:
        first_cross_thresh_time_stamp = -1
        # Ignore samples that occured before the flash
        for sample in samples:
            if sample[0] < flash_time_stamp:    # ---CHANGE HERE---
                continue
            if sample[1] < threshold:
                first_cross_thresh_time_stamp = sample[0]
                break

        # Save
        first_thresh_cross_time_stamps.append(first_cross_thresh_time_stamp)
        delays.append(first_cross_thresh_time_stamp - flash_time_stamp)
    
    return first_thresh_cross_time_stamps, delays

What am I doing wrong here?

Upvotes: 0

Views: 72

Answers (2)

Ali Asghari
Ali Asghari

Reputation: 112

When you iterate over zip object it pops all of it data so second time this is an empty list samples_filtered = [s for s in samples if s[0] >= flash_time_stamp] so you can zip data on the go like this:

samples_filtered = [s for s in zip(sample_time_stamps, sample_values) if s[0] >= flash_time_stamp]

Upvotes: 1

Sayse
Sayse

Reputation: 43300

I wouldn't even use a list comprehension here, the second snippet would be more efficient since you're not creating a list unnecessarily if the first elements make you break the loop early on.

You could just use next and replace the entire for loop altogether

first_cross_thresh_time_stamp = next(
    (s[0] for s in samples if s[0] >= flash_time_stamp and s[1] < threshold),
    -1
)
first_thresh_cross_time_stamps.append(first_cross_thresh_time_stamp)

Upvotes: 1

Related Questions