Non-short-circuiting position()?

The position() function from Iterator short-circuits. What's the rustacean way to do what position() does, but to get the indices of all elements in the iterator that return true?

fn main() {
    let a = vec![-2,-1,1,2];
    let first_positive = a.iter().position(|&x| x > 0).unwrap();
    println!("{:#?}", first_positive)
    
    // let all_positives = ??
   // should return [2,3]
}

Nothing builtin that I know of, but you can use enumerate and then filter/map:

let all_positives = a
    .iter()
    .enumerate()
    .filter(|(_, x)| **x > 0)
    .map(|(idx, _)| idx);

That gets you an Iterator<Item = usize>. If you need to do this a lot, you could make a function:

fn all_positions<I, P, T>(iter: I, mut pred: P) -> impl Iterator<Item = usize>
where
    I: Iterator<Item = T>,
    P: FnMut(&T) -> bool,
{
    iter.enumerate()
        .filter(move |(_, x)| pred(x))
        .map(|(idx, _)| idx)
}
let all_positives = all_positions(a.iter(), |x| **x > 0);
3 Likes

Itertools crate also has something for this:

https://docs.rs/itertools/0.10.0/itertools/trait.Itertools.html#method.positions

1 Like

You can further simplify that to:

iter.enumerate()
    .filter_map(|(i, x)| pred(x).then(|| i))

You could also relax the I: Iterator bound to IntoIterator so as to make the interface a little friendlier.

6 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.