How to generate iterator with sliding window pairs?

3.9k views Asked by At

I'd like to create an iterator that for this input:

[1, 2, 3, 4]

Will contain the following:

(1, 2)
(2, 3)
(3, 4)

Peekable seems ideal for this, but I'm new to Rust, so this naïve version doesn't work:

fn main() {
  let i = ['a', 'b', 'c']
    .iter()
    .peekable();
  let j = i.map(|x| (x, i.peek()));
  println!("{:?}", j);
  println!("Hello World!");
}

What am I doing wrong?

2

There are 2 answers

1
pretzelhammer On BEST ANSWER

You can use the windows method on slices, and then map the arrays into tuples:

fn main() {
    let i = [1, 2, 3, 4]
        .windows(2)
        .map(|pair| (pair[0], pair[1]));
    println!("{:?}", i.collect::<Vec<_>>());
}

playground


If you want a solution that works for all iterators (and not just slices) and are willing to use a 3rd-party library you can use the tuple_windows method from itertools.

use itertools::{Itertools, TupleWindows}; // 0.10.0

fn main() {
    let i: TupleWindows<_, (i32, i32)> = vec![1, 2, 3, 4]
        .into_iter()
        .tuple_windows();
    println!("{:?}", i.collect::<Vec<_>>());
}

playground


If you're not willing to use a 3rd-party library it's still simple enough that you can implement it yourself! Here's an example generic implementation that works for any Iterator<Item = T> where T: Clone:

use std::collections::BTreeSet;

struct PairIter<I, T>
where
    I: Iterator<Item = T>,
    T: Clone,
{
    iterator: I,
    last_item: Option<T>,
}

impl<I, T> PairIter<I, T>
where
    I: Iterator<Item = T>,
    T: Clone,
{
    fn new(iterator: I) -> Self {
        PairIter {
            iterator,
            last_item: None,
        }
    }
}

impl<I, T> Iterator for PairIter<I, T>
where
    I: Iterator<Item = T>,
    T: Clone,
{
    type Item = (T, T);
    fn next(&mut self) -> Option<Self::Item> {
        if self.last_item.is_none() {
            self.last_item = self.iterator.next();
        }
        if self.last_item.is_none() {
            return None;
        }
        let curr_item = self.iterator.next();
        if curr_item.is_none() {
            return None;
        }
        let temp_item = curr_item.clone();
        let result = (self.last_item.take().unwrap(), curr_item.unwrap());
        self.last_item = temp_item;
        Some(result)
    }
}

fn example<T: Clone>(iterator: impl Iterator<Item = T>) -> impl Iterator<Item = (T, T)> {
    PairIter::new(iterator)
}

fn main() {
    let mut set = BTreeSet::new();
    set.insert(String::from("a"));
    set.insert(String::from("b"));
    set.insert(String::from("c"));
    set.insert(String::from("d"));
    dbg!(example(set.into_iter()).collect::<Vec<_>>());
}

playground

0
kmdreko On

You can use tuple_windows() from the itertools crate as a drop-in replacement:

use itertools::Itertools;

fn main() {
    let data = vec![1, 2, 3, 4];
    for (a, b) in data.iter().tuple_windows() {
        println!("({}, {})", a, b);
    }
}
(1, 2)
(2, 3)
(3, 4)