Create an iterator like zip that takes iterators of specific items

I have some vectors consisting on TimeValue tuples ie type TimeValue = (DateTime, f64);
Now I want to add these types of vectors together. However, the datetime values have to match between vectors . For example data like below. Missing data has to be ignored.

        let d1 = Utc.ymd(2019, 12, 9).and_hms(1, 1, 0);
        let d2 = Utc.ymd(2019, 12, 9).and_hms(1, 2, 0);
        let d3 = Utc.ymd(2019, 12, 9).and_hms(1, 3, 0);
        let d4 = Utc.ymd(2019, 12, 9).and_hms(1, 4, 0);
        let d5 = Utc.ymd(2019, 12, 9).and_hms(1, 5, 0);
        let d6 = Utc.ymd(2019, 12, 9).and_hms(1, 6, 0);
        let d7 = Utc.ymd(2019, 12, 9).and_hms(1, 7, 0);

        let lhs_values = vec![(d1, 1.0), (d3, 3.0), (d4, 4.0), (d5, 5.0), (d6, 6.0)]; 
        let rhs_values = vec![(d1, 1.0), (d2, 2.0), (d3, 3.0), (d5, 5.0), (d6, 6.0), (d7, 7.0)];

The following code seems to work. (truncate is a custom method. Just assume datetimes are truncated to the minute say)

        let mut it_lhs = lhs_values.iter();
        let mut it_rhs = rhs_values.iter();

        let mut it_lhs_value = it_lhs.next();
        let mut it_rhs_value = it_rhs.next();

        loop {
            match (it_lhs_value, it_rhs_value) {
                (Some(lhs_row), Some(rhs_row)) => {

                    let lhs_dt: DateTime<Utc> = lhs_row.0.truncate(chrono::Duration::minutes(1));
                    let rhs_dt: DateTime<Utc> = rhs_row.0.truncate(chrono::Duration::minutes(1));
     
                     if lhs_dt > rhs_dt {
                        it_rhs_value = it_rhs.next();
                        continue;
                     }
                     else if rhs_dt > lhs_dt {
                        it_lhs_value = it_lhs.next();
                        continue;
                     }
                     else {

                        // Do what I need here as the datetimes match for the pair

                        it_rhs_value = it_rhs.next();
                        it_lhs_value = it_lhs.next();
                     }
                },
                (Some(lhs_row), None) => {
                    it_lhs_value = it_lhs.next();
                    continue
                },  
                (None, Some(rhs_row)) => {
                    it_rhs_value = it_rhs.next();
                    continue
                },  
                (None, None) => break,
            }
        }

This is not that elegant though. I thought I may be able to create my own iterator that does this for me like zip. The zip code https://doc.rust-lang.org/src/core/iter/adapters/zip.rs.html#12 is quite complicated though . How can I define an iterator that takes two iterators of TimeValue values ?

So far I have

pub struct TimeValueZip<A> {
    a: A,
    b: A
}

impl<A: Iterator,> TimeValueZip<A> {
    pub fn new(a: A, b: A) -> TimeValueZip<A> {
        TimeValueZip{
            a: a,
            b: b
        }
    }
}

impl<A> Iterator for TimeValueZip<A> where A: Iterator
{
    type Item = (A::Item, A::Item);
    
    // Here, we define the sequence using `.curr` and `.next`.
    // The return type is `Option<T>`:
    //     * When the `Iterator` is finished, `None` is returned.
    //     * Otherwise, the next value is wrapped in `Some` and returned.
    fn next(&mut self) -> Option<Self::Item> {
    
        
    }
}

How do I specify that the iterators have to be iterators to TimeValues ?

Thanks

You can specify associated types in the trait bound, A: Iterator<Item = TimeValue>

1 Like