Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove only the trailing elements of a Vec that match a condition

Tags:

iterator

rust

I have a Vec<T> that has elements matching a pattern. I want to remove all trailing instances of the elements that match the pattern.

For example, I have a Vec<i32> and the pattern is (|x| x == 0). If the input was: vec![0, 1, 0, 2, 3, 0, 0], the output should be: vec![0, 1, 0, 2, 3]

To do this I tried:

fn main() {
    let mut vec = vec![0, 1, 0, 2, 3, 0, 0];
    vec = vec.into_iter().rev().skip_while(|&x| x == 0).rev();
}

But I get these compiler errors:

error[E0277]: the trait bound `std::iter::SkipWhile<std::iter::Rev<std::vec::IntoIter<{integer}>>, [closure@src/main.rs:3:44: 3:55]>: std::iter::DoubleEndedIterator` is not satisfied
 --> src/main.rs:3:57
  |
3 |     vec = vec.into_iter().rev().skip_while(|&x| x == 0).rev();
  |                                                         ^^^ the trait `std::iter::DoubleEndedIterator` is not implemented for `std::iter::SkipWhile<std::iter::Rev<std::vec::IntoIter<{integer}>>, [closure@src/main.rs:3:44: 3:55]>`

error[E0308]: mismatched types
 --> src/main.rs:3:11
  |
3 |     vec = vec.into_iter().rev().skip_while(|&x| x == 0).rev();
  |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::vec::Vec`, found struct `std::iter::Rev`
  |
  = note: expected type `std::vec::Vec<{integer}>`
             found type `std::iter::Rev<std::iter::SkipWhile<std::iter::Rev<std::vec::IntoIter<{integer}>>, [closure@src/main.rs:3:44: 3:55]>>`

The strange thing is that DoubleEndedIterator is implemented for SkipWhile. In fact, SkipWhile even implements rev(). See here.

What am I doing wrong? Is there a better approach?

like image 923
HiDefender Avatar asked Dec 10 '22 08:12

HiDefender


1 Answers

The iterator adaptor for reversal only works for iterators which can be traversed from any of the two ends (as in, it is a DoubleEndedIterator). While this is the case for the original one, this is no longer possible from the moment we include skip_while. In order to follow that approach, you would have to collect the rest of the reversed vector, and only then reverse again.

I, on the other hand, would just choose to fetch the index of the first trailing zero and truncate the vector with it.

let mut vec = vec![0, 1, 0, 2, 3, 0, 0];
if let Some(i) = vec.iter().rposition(|x| *x != 0) {
    let new_len = i + 1;
    vec.truncate(new_len);
}

... or just grab a slice instead:

let piece = &vec[..new_len];

Playground

like image 165
E_net4 stands with Ukraine Avatar answered Jun 15 '23 12:06

E_net4 stands with Ukraine