简体   繁体   中英

is it possible to filter on a vector in-place?

I'd like to remove some elements from a Vec , but vec.iter().filter().collect() creates a new vector with borrowed items.

I'd like to mutate the original Vec without extra memory allocation (and keep memory of removed elements as an extra capacity of the vector).

If you want to remove elements, you can use retain() , which removes elements from the vector if the closure returns false :

let mut vec = vec![1, 2, 3, 4];
vec.retain(|&x| x % 2 == 0);
assert_eq!(vec, [2, 4]);

If you want to modify the elements in place, you have to do that in a for x in vec.iter_mut() .

If you truly want to mutate the vector's elements while filtering it, you can use the nightly-only method Vec::drain_filter , an extremely flexible tool:

#![feature(drain_filter)]

fn main() {
    let mut vec = vec![1, 2, 3, 4];
    vec.drain_filter(|x| {
        if *x % 2 == 0 {
            true
        } else {
            *x += 100;
            false
        }
    });
    assert_eq!(vec, [101, 103]);
}

It also allows you to get the removed elements as the return value of the entire method is an iterator!

Till Vec::drain_filter gets stable, we can solve the problem with homebrewed rust:

fn main() {
    let mut v = vec![1, 2, 3, 4];
    let mut i = 0;
    while i < v.len() {
        if v[i] % 2 == 0 {
            v.remove(i);
        } else {
            v[i] += 100;
            i += 1;
        }
    }
    println!("{:?}", v);  // [101, 103]
}

BTW remove() is an O(n) operation but doesn't allocate memory.
Playground

I am providing my take for this problem as I was unaware of the retain method:

impl<T> RemoveFilter<T> for Vec<T> {}
pub trait RemoveFilter<T>: BorrowMut<Vec<T>> {
    fn remove_filter<F: for<'b> FnMut(&'b T) -> bool>(&mut self, mut cb: F) {
        let vec: &mut Vec<T> = self.borrow_mut();
        let mut write_to = 0;
        let mut read_from = 0;
        while read_from < vec.len() {
            let maintain = cb(&mut vec[read_from]);
            if maintain {
                vec.as_mut_slice().swap(read_from, write_to);
                write_to += 1;
            }
            read_from += 1;
        }
        vec.resize_with(write_to, || panic!("We are shrinking the vector"));
    }
}

It will shift the elements as it iterates and then remove anything that is left behind. I think this is code may easily modified to solve other problems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM