简体   繁体   中英

How can I filter a vector on an index in Rust

I have a Vec<f64> and I am trying to get say every 7th element of the vector till I run out of bounds, into another Vec<f64> . I thought maybe I could create an index of the elements I want and then filter based on that. But I can't seem to be able to do this directly or indirectly. What I tried

let x: Vec<f64> = (1..200)
.map(|x| (x as f64)/(200 as f64))
.collect();

let y: Vec<f64> = x
.enumerate()
.filter(|&(i, _)| i % 7 == 0 )
.map(|(_, e)| e)
.collect();

But this did not work with compile error enumerate method cannot be called on Vec<f64> due to unsatisfied trait bounds . I also found a retain method but don't see a way to apply it on the index rather than the element. A robust search of SO surprisingly did not yield anything.

Thanks for the comments, patching everything together into a more complete answer for the community. Let's say this is the Vec: let x: Vec<f64> = (1..10).map(|x| (x as f64)/(10 as f64)).collect();

To filter the vector based on index, first we create an iterator with into_iter, then enumerate it to get index, then apply the filter, and then a map to remove the index, finally collecting it to f64 vector.

let y: Vec<f64> = x
.into_iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 0 )
.map(|(_, e)| e)
.collect();

If the scope of y is shorter than that of x , and if you had large values in y (say string), it might be preferable to make y a vector of references rather than values.

let y: Vec<&f64> = x
.iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 0 )
.map(|(_, e)| e)
.collect();

The key difference here is using iter() instead of iter_into() . This page in rust book explains it:

The iter method produces an iterator over immutable references. If we want to create an iterator that takes ownership of v1 and returns owned values, we can call into_iter instead of iter . Similarly, if we want to iterate over mutable references, we can call iter_mut instead of iter .

Now, for this specific question, applying a filter to the index is probably not needed. A simpler way, as noted by Chayim below is

let y: Vec<_> = x.into_iter().step_by(2).collect();

I also found a retain method but don't see a way to apply it on the index rather than the element.

While Vec::retain does not give you any sort of index, it takes an FnMut and is documented to operate in-order:

This method operates in place, visiting each element exactly once in the original order , and preserves the order of the retained elements.

So you can just keep track of the index yourself:

let mut idx = 0;
x.retain(|_| {
    let v = idx;
    idx += 1;
    v % 2 == 0
});

Or here you can specialise with a simple toggle:

let mut keep = false;
x.retain(|_| {
    keep = !keep;
    keep
});

请注意,有一个专门的迭代器适配器step_by()

let y: Vec<_> = x.into_iter().step_by(7).collect();

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