diff --git a/src/lib.rs b/src/lib.rs index 6e86ab789..057920aed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,6 +146,7 @@ pub mod structs { pub use crate::repeatn::RepeatN; #[allow(deprecated)] pub use crate::sources::{RepeatCall, Unfold, Iterate}; + pub use crate::take_while_inclusive::TakeWhileInclusive; #[cfg(feature = "use_alloc")] pub use crate::tee::Tee; pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples}; @@ -233,6 +234,7 @@ mod rciter_impl; mod repeatn; mod size_hint; mod sources; +mod take_while_inclusive; #[cfg(feature = "use_alloc")] mod tee; mod tuple_impl; @@ -904,7 +906,7 @@ pub trait Itertools : Iterator { /// Return an iterator adaptor that flattens every `Result::Ok` value into /// a series of `Result::Ok` values. `Result::Err` values are unchanged. - /// + /// /// This is useful when you have some common error type for your crate and /// need to propagate it upwards, but the `Result::Ok` case needs to be flattened. /// @@ -914,7 +916,7 @@ pub trait Itertools : Iterator { /// let input = vec![Ok(0..2), Err(false), Ok(2..4)]; /// let it = input.iter().cloned().flatten_ok(); /// itertools::assert_equal(it.clone(), vec![Ok(0), Ok(1), Err(false), Ok(2), Ok(3)]); - /// + /// /// // This can also be used to propagate errors when collecting. /// let output_result: Result, bool> = it.collect(); /// assert_eq!(output_result, Err(false)); @@ -1389,6 +1391,74 @@ pub trait Itertools : Iterator { adaptors::take_while_ref(self, accept) } + /// Returns an iterator adaptor that consumes elements while the given + /// predicate is `true`, *including* the element for which the predicate + /// first returned `false`. + /// + /// The [`.take_while()`][std::iter::Iterator::take_while] adaptor is useful + /// when you want items satisfying a predicate, but to know when to stop + /// taking elements, we have to consume that first element that doesn't + /// satisfy the predicate. This adaptor includes that element where + /// [`.take_while()`][std::iter::Iterator::take_while] would drop it. + /// + /// The [`.take_while_ref()`][crate::Itertools::take_while_ref] adaptor + /// serves a similar purpose, but this adaptor doesn't require [`Clone`]ing + /// the underlying elements. + /// + /// ```rust + /// # use itertools::Itertools; + /// let items = vec![1, 2, 3, 4, 5]; + /// let filtered: Vec<_> = items + /// .into_iter() + /// .take_while_inclusive(|&n| n % 3 != 0) + /// .collect(); + /// + /// assert_eq!(filtered, vec![1, 2, 3]); + /// ``` + /// + /// ```rust + /// # use itertools::Itertools; + /// let items = vec![1, 2, 3, 4, 5]; + /// + /// let take_while_inclusive_result: Vec<_> = items + /// .iter() + /// .copied() + /// .take_while_inclusive(|&n| n % 3 != 0) + /// .collect(); + /// let take_while_result: Vec<_> = items + /// .into_iter() + /// .take_while(|&n| n % 3 != 0) + /// .collect(); + /// + /// assert_eq!(take_while_inclusive_result, vec![1, 2, 3]); + /// assert_eq!(take_while_result, vec![1, 2]); + /// // both iterators have the same items remaining at this point---the 3 + /// // is lost from the `take_while` vec + /// ``` + /// + /// ```rust + /// # use itertools::Itertools; + /// #[derive(Debug, PartialEq)] + /// struct NoCloneImpl(i32); + /// + /// let non_clonable_items: Vec<_> = vec![1, 2, 3, 4, 5] + /// .into_iter() + /// .map(NoCloneImpl) + /// .collect(); + /// let filtered: Vec<_> = non_clonable_items + /// .into_iter() + /// .take_while_inclusive(|n| n.0 % 3 != 0) + /// .collect(); + /// let expected: Vec<_> = vec![1, 2, 3].into_iter().map(NoCloneImpl).collect(); + /// assert_eq!(filtered, expected); + fn take_while_inclusive(&mut self, accept: F) -> TakeWhileInclusive + where + Self: Sized, + F: FnMut(&Self::Item) -> bool, + { + take_while_inclusive::TakeWhileInclusive::new(self, accept) + } + /// Return an iterator adaptor that filters `Option` iterator elements /// and produces `A`. Stops on the first `None` encountered. /// @@ -1812,14 +1882,14 @@ pub trait Itertools : Iterator { /// /// #[derive(PartialEq, Debug)] /// enum Enum { A, B, C, D, E, } - /// + /// /// let mut iter = vec![Enum::A, Enum::B, Enum::C, Enum::D].into_iter(); - /// + /// /// // search `iter` for `B` /// assert_eq!(iter.contains(&Enum::B), true); /// // `B` was found, so the iterator now rests at the item after `B` (i.e, `C`). /// assert_eq!(iter.next(), Some(Enum::C)); - /// + /// /// // search `iter` for `E` /// assert_eq!(iter.contains(&Enum::E), false); /// // `E` wasn't found, so `iter` is now exhausted @@ -2694,7 +2764,6 @@ pub trait Itertools : Iterator { /// itertools::assert_equal(oldest_people_first, /// vec!["Jill", "Jack", "Jane", "John"]); /// ``` - /// ``` #[cfg(feature = "use_alloc")] fn sorted_by_cached_key(self, f: F) -> VecIntoIter where @@ -2870,13 +2939,13 @@ pub trait Itertools : Iterator { group_map::into_group_map_by(self, f) } - /// Constructs a `GroupingMap` to be used later with one of the efficient + /// Constructs a `GroupingMap` to be used later with one of the efficient /// group-and-fold operations it allows to perform. - /// + /// /// The input iterator must yield item in the form of `(K, V)` where the /// value of type `K` will be used as key to identify the groups and the /// value of type `V` as value for the folding operation. - /// + /// /// See [`GroupingMap`] for more informations /// on what operations are available. #[cfg(feature = "use_std")] @@ -2887,12 +2956,12 @@ pub trait Itertools : Iterator { grouping_map::new(self) } - /// Constructs a `GroupingMap` to be used later with one of the efficient + /// Constructs a `GroupingMap` to be used later with one of the efficient /// group-and-fold operations it allows to perform. - /// + /// /// The values from this iterator will be used as values for the folding operation /// while the keys will be obtained from the values by calling `key_mapper`. - /// + /// /// See [`GroupingMap`] for more informations /// on what operations are available. #[cfg(feature = "use_std")] @@ -3603,7 +3672,7 @@ pub trait Itertools : Iterator { /// first_name: &'static str, /// last_name: &'static str, /// } - /// + /// /// let characters = /// vec![ /// Character { first_name: "Amy", last_name: "Pond" }, @@ -3614,12 +3683,12 @@ pub trait Itertools : Iterator { /// Character { first_name: "James", last_name: "Norington" }, /// Character { first_name: "James", last_name: "Kirk" }, /// ]; - /// - /// let first_name_frequency = + /// + /// let first_name_frequency = /// characters /// .into_iter() /// .counts_by(|c| c.first_name); - /// + /// /// assert_eq!(first_name_frequency["Amy"], 3); /// assert_eq!(first_name_frequency["James"], 4); /// assert_eq!(first_name_frequency.contains_key("Asha"), false); @@ -3640,7 +3709,7 @@ pub trait Itertools : Iterator { /// column. /// /// This function is, in some sense, the opposite of [`multizip`]. - /// + /// /// ``` /// use itertools::Itertools; /// diff --git a/src/take_while_inclusive.rs b/src/take_while_inclusive.rs new file mode 100644 index 000000000..e2a7479e0 --- /dev/null +++ b/src/take_while_inclusive.rs @@ -0,0 +1,68 @@ +use core::iter::FusedIterator; +use std::fmt; + +/// An iterator adaptor that consumes elements while the given predicate is +/// `true`, including the element for which the predicate first returned +/// `false`. +/// +/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive) +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct TakeWhileInclusive<'a, I: 'a, F> { + iter: &'a mut I, + predicate: F, + done: bool, +} + +impl<'a, I, F> TakeWhileInclusive<'a, I, F> +where + I: Iterator, + F: FnMut(&I::Item) -> bool, +{ + /// Create a new [`TakeWhileInclusive`] from an iterator and a predicate. + pub fn new(iter: &'a mut I, predicate: F) -> Self { + Self { iter, predicate, done: false} + } +} + +impl<'a, I, F> fmt::Debug for TakeWhileInclusive<'a, I, F> + where I: Iterator + fmt::Debug, +{ + debug_fmt_fields!(TakeWhileInclusive, iter); +} + +impl<'a, I, F> Iterator for TakeWhileInclusive<'a, I, F> +where + I: Iterator, + F: FnMut(&I::Item) -> bool +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + if self.done { + None + } else { + self.iter.next().map(|item| { + if !(self.predicate)(&item) { + self.done = true; + } + item + }) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.done { + (0, Some(0)) + } else { + (0, self.iter.size_hint().1) + } + } +} + +impl FusedIterator for TakeWhileInclusive<'_, I, F> +where + I: Iterator, + F: FnMut(&I::Item) -> bool +{ +} \ No newline at end of file