4

I'm trying to figure out how to return a window of elements from a vector that I've first filtered without copying it to a new vector.

So this is the naive approach which works fine but I think will end up allocating a new vector from line 5 which I don't really want to do.

let mut buf  = Vec::new();
file.read_to_end(&mut buf);
// Do some filtering of the read file and create a new vector for subsequent processing
let iter = buf.iter().filter(|&x| *x != 10 && *x != 13);
let clean_buf = Vec::from_iter(iter);
for iter in clean_buf.windows(13) {
    print!("{}",iter.len());
}

Alternative approach where I could use a chain()? to achieve the same thing without copying into a new Vec

for iter in buf.iter().filter(|&x| *x != 10 && *x != 13) {
    let window =  ???       
}
Delta_Fore
  • 3,079
  • 4
  • 26
  • 46

2 Answers2

4

You can use Vec::retain instead of filter for this, which allows you to keep your Vec:

fn main() {
    let mut buf = vec![
        8, 9, 10, 11, 12, 13, 14,
        8, 9, 10, 11, 12, 13, 14,
        8, 9, 10, 11, 12, 13, 14,
    ];
    println!("{:?}", buf);
    buf.retain(|&x| x != 10 && x != 13);
    println!("{:?}", buf);
    for iter in buf.windows(13) {
        print!("{}, ", iter.len());
    }
    println!("");
}
DK.
  • 55,277
  • 5
  • 189
  • 162
1

I don't see how this would be possible. You say:

elements from a vector that I've first filtered

But once you've filtered a vector, you don't have a vector anymore - you just have an Iterator. Iterators only have the concept of the next item.

To be most efficient, you'd have to create a small buffer of the size of your window. Unfortunately, you cannot write an iterator that returns a reference to itself, so you'd have to pass in a buffer to a hypothetical Iterator::windows method. In that case, you'd run into the problem of having a mutable reference (so you could populate the buffer) and an immutable reference (so you could return a slice), which won't fly.

The only close solution I can think of is to have multiple iterators over the same vector that you then zip together:

fn main() {
    let nums: Vec<u8> = (1..100).collect();

    fn is_even(x: &&u8) -> bool { **x % 2 == 0 }

    let a = nums.iter().filter(is_even);
    let b = nums.iter().filter(is_even).skip(1);
    let c = nums.iter().filter(is_even).skip(2);

    for z in a.zip(b).zip(c).map(|((a, b), c)| (a,b,c)) {
        println!("{:?}", z);
    }  
}

This has the distinct downside of needing to apply the filtering condition multiple times, and the ugliness of the nested zips (you can fix the latter with use of itertools though).

Personally, I'd probably just collect into a Vec, as you have already done.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366