Explain filter method

fn main() {
    let months = vec!["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

    let filtered_months = months
        .into_iter()                     
        .filter(|month| month.len() < 5)
        .filter(|month| month.contains("u"))
        .collect::<Vec<&str>>();

    println!("{:?}", filtered_months);
}

1.what happens when calling each method describedabove

Hi, make sure to take a look at

1 Like

For general information about iterators, if you like watching a video, I could recommend the following resource:

1 Like

The into_iter method creates an iterator for the vector months.

The filter method wraps the iterator with the Filter iterator adapter. This adapter is an iterator that only returns some of the items returned by the original/wrapped iterator, based on a predicate. It’s basically a struct

struct Filter<I, P> {
    iterator: I,
    predicate: P,
}

that implements Iterator

impl<I, P> Iterator for Filter<I, P>
where
    I: Iterator,
    P: FnMut(&I::Item) -> bool,
{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.iterator.next() {
                None => return None,
                Some(item) if (self.predicate)(&item) => return Some(item),
                _ => continue, // Some(item) not matching predicate
            }
        }
    }
}

This could be used directly as follows

fn main() {
    let months = vec![
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
    ];
    
    let months_iterator = months.into_iter();
    let first_filter = Filter {
        iterator: months_iterator,
        predicate: |month: &&str| month.len() < 5
    };
    let second_filter = Filter {
        iterator: first_filter,
        predicate: |month: &&str| month.contains("u")
    };
    let filtered_months = second_filter.collect::<Vec<&str>>();
    
    println!("{:?}", filtered_months);
}

(playground)

Or with a helper function

fn filter<I, P>(iterator: I, predicate: P) -> Filter<I, P>
where
    I: Iterator,
    P: FnMut(&I::Item) -> bool,
{
    Filter {
        iterator,
        predicate,
    }
}
[…]
let filtered_months = filter(
    filter(months.into_iter(), |month| month.len() < 5),
    |month| month.contains("u"),
)
.collect::<Vec<&str>>();

The method Iterator::filter is basically the same as such a helper function, but it supports method-call notation, i.e. iterator.filter(predicate) instead of filter(iterator, predicate).

Finally, collect walks through this iterator (of type β€œFilter<Filter<vec::IntoIter<&str>, {closure type …}>, {closure type …}>”) and collects all its items into a vector. Writing this manually would look something like

fn collect_vector<I>(mut iterator: I) -> Vec<I::Item>
where
    I: Iterator,
{
    let mut v = vec![];
    while let Some(item) = iterator.next() {
        v.push(item);
    }
    v
}

which could be used as

let filtered_months = collect_vector(filter(
    filter(months.into_iter(), |month| month.len() < 5),
    |month| month.contains("u"),
));

(playground)

7 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.