Filtering a vector with options inside

Hi there,

I have two structures like this:

#[derive(Clone)]
struct A {
  b: Option<B>
}

#[derive(Clone)]
struct B {
  ok: Option<bool>
}

Then I have a Vec<A> in which I'd like to iterate over all elements that have ok == true. Currently I'm doing the following, but I'm sure (I hope) this can be optimized:

let va = vec![...here be some values...];
let va_ok: Vec<A> = va.iter()
  .filter(|a| a.b.is_some())
  .filter(|a| a.b.as_ref().unwrap().ok.unwrap_or(false))
  .map(|a| a.clone())
  .collect()

I'd like to combine the two filter calls, and know if there is a better way than to use the map to get back the values, and not the references.

let va_ok: Vec<_> = va.iter()
  .filter(|a| a.b.as_ref().map_or(false, |b| b.ok.unwrap_or(false)))
  .cloned()
  .collect()
1 Like

Check out cloned(), flat_map() and filter_map() methods
Also don't worry about combining filter calls, iterators are lazy so it's still one loop with minimal overhead. Usually like to make many small iterator combinator calls for clarity

Thanks a lot, that works!

I'm still wrapping my mind around all these little tricks. It's really what I missed in go all these years, but now that I have all the possibilities, I understand why go wants to be simple - it can be quite overwhelming :wink:

let va_ok: Vec<A> = va
    .iter()
    .filter(|a| matches!(a.b, Some(B { ok: Some(true), .. })))
    .cloned()
    .collect();

using the .. pattern here under the assumption that in the real use case there are some extra fields.

Another alternative:

let va_ok: Vec<A> = va
    .iter()
    .filter(|a| matches!(&a.b, Some(b) if b.ok == Some(true)))
    .cloned()
    .collect();
4 Likes

Wow - very nice! I prefer the second one in fact.

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.