Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing every element in a vector with the next one

Tags:

rust

I want to compare every element of a vector with the next one. I found the following solution but it is not so readable.

struct Obj {
    i: u32,
}

impl Obj {
    fn is_valid_next(&self, next: &Obj) -> bool {
        self.i + 1 == next.i
    }
}

fn main() {
    let data: Vec<Obj> = vec![Obj { i: 0 }, Obj { i: 2 }, Obj { i: 2 }];
    let mut previous: Option<Obj> = None;
    let mut is_valid = true;
    for current in data.into_iter() {
        match previous {
            Some(p) => {
                is_valid = p.is_valid_next(&current);
                if !is_valid {
                    break;
                }
            }
            None => {}
        }
        previous = Some(current);
    }
    println!("is valid {}", is_valid);
}
like image 834
gontard Avatar asked Dec 05 '22 09:12

gontard


1 Answers

This can be done in several ways.

Using slice::windows

slice::windows:

Returns an iterator over all contiguous windows of length size.

In this case, the window size will be 2 so windows will return an iterator which will mask 2 elements and it will move to the right on every iteration.

initial position : [|0, 5|, 10, 40, 30] // &[0, 5]
next position    : [0, |5, 10|, 40, 30] // &[5, 10]

Here is an applied solution to your problem:

fn main() {
    let data: Vec<Obj> = vec![Obj { i: 0 }, Obj { i: 1 }, Obj { i: 2 }, Obj { i: 3 }];

    let is_valid = data.windows(2).all(|w| w[0].is_valid_next(&w[1]));

    println!("Is consecutive -> {:?}", is_valid); // true
}

Playground

Please note that direct index access of a slice might cause a panic, but since the produced slice from Windows iterator will be the same size as the argument to windows, it will be fine to access elements directly by index as long as the index is less than the window size.

Using Iterator::zip

You can compare the current element with the next one by using two different iterators of same Vec with std::iter::Zip.

The iterator elements from the first iterator (data.iter()) will represent the current element. Elements from the second iterator (data.iter().skip(1)) will represent the next element.

Here is the implementation:

fn main() {
    let data: Vec<Obj> = vec![Obj { i: 0 }, Obj { i: 1 }, Obj { i: 2 }];

    let is_valid = data
        .iter()
        .zip(data.iter().skip(1))
        .all(|(current, next)| current.is_valid_next(next));

    println!("Is consecutive -> {:?}", is_valid); // true
}

Playground

If you are sure your Vec is not empty, you can use slices to create the iterators: &data[1..] instead of data.iter().skip(1) (sample). This will panic if you have an empty Vec.

like image 59
Ömer Erden Avatar answered Feb 14 '23 10:02

Ömer Erden