Skip to content

Commit c5b64c9

Browse files
bors[bot]Erik Rhodes
and
Erik Rhodes
authored
Merge #616
616: Add `take_while_inclusive` method r=jswrenn a=e-rhodes Adds `take_while_inclusive` method that returns elements as long as a predicate is satisfied, but including the first element that doesn't satisfy the predicate. First discussed addition to std in <rust-lang/rust#62208>. Closes #597. Co-authored-by: Erik Rhodes <[email protected]>
2 parents eb561e5 + 91fe99e commit c5b64c9

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

src/lib.rs

+70
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub mod structs {
146146
pub use crate::repeatn::RepeatN;
147147
#[allow(deprecated)]
148148
pub use crate::sources::{RepeatCall, Unfold, Iterate};
149+
pub use crate::take_while_inclusive::TakeWhileInclusive;
149150
#[cfg(feature = "use_alloc")]
150151
pub use crate::tee::Tee;
151152
pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples};
@@ -233,6 +234,7 @@ mod rciter_impl;
233234
mod repeatn;
234235
mod size_hint;
235236
mod sources;
237+
mod take_while_inclusive;
236238
#[cfg(feature = "use_alloc")]
237239
mod tee;
238240
mod tuple_impl;
@@ -1457,6 +1459,74 @@ pub trait Itertools : Iterator {
14571459
adaptors::take_while_ref(self, accept)
14581460
}
14591461

1462+
/// Returns an iterator adaptor that consumes elements while the given
1463+
/// predicate is `true`, *including* the element for which the predicate
1464+
/// first returned `false`.
1465+
///
1466+
/// The [`.take_while()`][std::iter::Iterator::take_while] adaptor is useful
1467+
/// when you want items satisfying a predicate, but to know when to stop
1468+
/// taking elements, we have to consume that first element that doesn't
1469+
/// satisfy the predicate. This adaptor includes that element where
1470+
/// [`.take_while()`][std::iter::Iterator::take_while] would drop it.
1471+
///
1472+
/// The [`.take_while_ref()`][crate::Itertools::take_while_ref] adaptor
1473+
/// serves a similar purpose, but this adaptor doesn't require [`Clone`]ing
1474+
/// the underlying elements.
1475+
///
1476+
/// ```rust
1477+
/// # use itertools::Itertools;
1478+
/// let items = vec![1, 2, 3, 4, 5];
1479+
/// let filtered: Vec<_> = items
1480+
/// .into_iter()
1481+
/// .take_while_inclusive(|&n| n % 3 != 0)
1482+
/// .collect();
1483+
///
1484+
/// assert_eq!(filtered, vec![1, 2, 3]);
1485+
/// ```
1486+
///
1487+
/// ```rust
1488+
/// # use itertools::Itertools;
1489+
/// let items = vec![1, 2, 3, 4, 5];
1490+
///
1491+
/// let take_while_inclusive_result: Vec<_> = items
1492+
/// .iter()
1493+
/// .copied()
1494+
/// .take_while_inclusive(|&n| n % 3 != 0)
1495+
/// .collect();
1496+
/// let take_while_result: Vec<_> = items
1497+
/// .into_iter()
1498+
/// .take_while(|&n| n % 3 != 0)
1499+
/// .collect();
1500+
///
1501+
/// assert_eq!(take_while_inclusive_result, vec![1, 2, 3]);
1502+
/// assert_eq!(take_while_result, vec![1, 2]);
1503+
/// // both iterators have the same items remaining at this point---the 3
1504+
/// // is lost from the `take_while` vec
1505+
/// ```
1506+
///
1507+
/// ```rust
1508+
/// # use itertools::Itertools;
1509+
/// #[derive(Debug, PartialEq)]
1510+
/// struct NoCloneImpl(i32);
1511+
///
1512+
/// let non_clonable_items: Vec<_> = vec![1, 2, 3, 4, 5]
1513+
/// .into_iter()
1514+
/// .map(NoCloneImpl)
1515+
/// .collect();
1516+
/// let filtered: Vec<_> = non_clonable_items
1517+
/// .into_iter()
1518+
/// .take_while_inclusive(|n| n.0 % 3 != 0)
1519+
/// .collect();
1520+
/// let expected: Vec<_> = vec![1, 2, 3].into_iter().map(NoCloneImpl).collect();
1521+
/// assert_eq!(filtered, expected);
1522+
fn take_while_inclusive<F>(&mut self, accept: F) -> TakeWhileInclusive<Self, F>
1523+
where
1524+
Self: Sized,
1525+
F: FnMut(&Self::Item) -> bool,
1526+
{
1527+
take_while_inclusive::TakeWhileInclusive::new(self, accept)
1528+
}
1529+
14601530
/// Return an iterator adaptor that filters `Option<A>` iterator elements
14611531
/// and produces `A`. Stops on the first `None` encountered.
14621532
///

src/take_while_inclusive.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use core::iter::FusedIterator;
2+
use std::fmt;
3+
4+
/// An iterator adaptor that consumes elements while the given predicate is
5+
/// `true`, including the element for which the predicate first returned
6+
/// `false`.
7+
///
8+
/// See [`.take_while_inclusive()`](crate::Itertools::take_while_inclusive)
9+
/// for more information.
10+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
11+
pub struct TakeWhileInclusive<'a, I: 'a, F> {
12+
iter: &'a mut I,
13+
predicate: F,
14+
done: bool,
15+
}
16+
17+
impl<'a, I, F> TakeWhileInclusive<'a, I, F>
18+
where
19+
I: Iterator,
20+
F: FnMut(&I::Item) -> bool,
21+
{
22+
/// Create a new [`TakeWhileInclusive`] from an iterator and a predicate.
23+
pub fn new(iter: &'a mut I, predicate: F) -> Self {
24+
Self { iter, predicate, done: false}
25+
}
26+
}
27+
28+
impl<'a, I, F> fmt::Debug for TakeWhileInclusive<'a, I, F>
29+
where I: Iterator + fmt::Debug,
30+
{
31+
debug_fmt_fields!(TakeWhileInclusive, iter);
32+
}
33+
34+
impl<'a, I, F> Iterator for TakeWhileInclusive<'a, I, F>
35+
where
36+
I: Iterator,
37+
F: FnMut(&I::Item) -> bool
38+
{
39+
type Item = I::Item;
40+
41+
fn next(&mut self) -> Option<Self::Item> {
42+
if self.done {
43+
None
44+
} else {
45+
self.iter.next().map(|item| {
46+
if !(self.predicate)(&item) {
47+
self.done = true;
48+
}
49+
item
50+
})
51+
}
52+
}
53+
54+
fn size_hint(&self) -> (usize, Option<usize>) {
55+
if self.done {
56+
(0, Some(0))
57+
} else {
58+
(0, self.iter.size_hint().1)
59+
}
60+
}
61+
}
62+
63+
impl<I, F> FusedIterator for TakeWhileInclusive<'_, I, F>
64+
where
65+
I: Iterator,
66+
F: FnMut(&I::Item) -> bool
67+
{
68+
}

0 commit comments

Comments
 (0)