ste_kwr
ste_kwr

Reputation: 1010

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.

Upvotes: 4

Views: 4500

Answers (3)

ste_kwr
ste_kwr

Reputation: 1010

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 into_iter(). 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();

Upvotes: 4

Chayim Friedman
Chayim Friedman

Reputation: 70970

Note that there is a specialized iterator adapter for that, step_by():

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

Upvotes: 2

Masklinn
Masklinn

Reputation: 42282

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
});

Upvotes: 1

Related Questions