diff --git a/library/core/src/iter/adapters/dedup.rs b/library/core/src/iter/adapters/dedup.rs new file mode 100644 index 000000000000..5113d8534e82 --- /dev/null +++ b/library/core/src/iter/adapters/dedup.rs @@ -0,0 +1,156 @@ +#![unstable(feature = "iter_dedup", reason = "recently added", issue = "83748")] + +/// An iterator that removes all but the first of consecutive elements in a +/// given iterator according to the [`PartialEq`] trait implementation. +/// +/// This `struct` is created by [`Iterator::dedup`]. +/// See its documentation for more. +/// +/// [`Iterator::dedup`]: Iterator::dedup +#[derive(Debug, Clone)] +pub struct Dedup +where + I: Iterator, +{ + inner: I, + last: Option>, +} + +impl Dedup +where + I: Iterator, +{ + #[inline] + pub(crate) fn new(inner: I) -> Self { + Self { inner, last: None } + } +} + +impl Iterator for Dedup +where + I: Iterator, + I::Item: PartialEq, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + let Self { inner, last } = self; + let last = last.get_or_insert_with(|| inner.next()); + let last_item = last.as_ref()?; + let next = inner.find(|next_item| next_item != last_item); + crate::mem::replace(last, next) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let min = matches!(self.last, Some(Some(_))).into(); + let max = self.inner.size_hint().1.map(|max| max + min); + (min, max) + } +} + +/// An iterator that removes all but the first of consecutive elements in a +/// given iterator satisfying a given equality relation. +/// +/// This `struct` is created by [`Iterator::dedup_by`]. +/// See its documentation for more. +/// +/// [`Iterator::dedup_by`]: Iterator::dedup_by +#[derive(Debug, Clone)] +pub struct DedupBy +where + I: Iterator, +{ + inner: I, + same_bucket: F, + last: Option>, +} + +impl DedupBy +where + I: Iterator, +{ + #[inline] + pub(crate) fn new(inner: I, same_bucket: F) -> Self { + Self { inner, same_bucket, last: None } + } +} + +impl Iterator for DedupBy +where + I: Iterator, + F: FnMut(&I::Item, &I::Item) -> bool, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + let Self { inner, last, same_bucket } = self; + let last = last.get_or_insert_with(|| inner.next()); + let last_item = last.as_ref()?; + let next = inner.find(|next_item| !(same_bucket)(next_item, last_item)); + crate::mem::replace(last, next) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let min = matches!(self.last, Some(Some(_))).into(); + let max = self.inner.size_hint().1.map(|max| max + min); + (min, max) + } +} + +/// An iterator that removes all but the first of consecutive elements in a +/// given iterator that resolve to the same key. +/// +/// This `struct` is created by [`Iterator::dedup_by_key`]. +/// See its documentation for more. +/// +/// [`Iterator::dedup_by_key`]: Iterator::dedup_by_key +#[derive(Debug, Clone)] +pub struct DedupByKey +where + I: Iterator, + F: FnMut(&I::Item) -> K, +{ + inner: I, + key: F, + last: Option>, +} + +impl DedupByKey +where + I: Iterator, + F: FnMut(&I::Item) -> K, +{ + #[inline] + pub(crate) fn new(inner: I, key: F) -> Self { + Self { inner, key, last: None } + } +} + +impl Iterator for DedupByKey +where + I: Iterator, + F: FnMut(&I::Item) -> K, + K: PartialEq, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + let Self { inner, last, key } = self; + let last = last.get_or_insert_with(|| inner.next()); + let last_item = last.as_ref()?; + let next = inner.find(|next_item| key(next_item) != key(last_item)); + crate::mem::replace(last, next) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let min = matches!(self.last, Some(Some(_))).into(); + let max = self.inner.size_hint().1.map(|max| max + min); + (min, max) + } +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index bf4fabad32a3..e40b7e3dad44 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -7,6 +7,7 @@ mod chain; mod cloned; mod copied; mod cycle; +mod dedup; mod enumerate; mod filter; mod filter_map; @@ -66,7 +67,10 @@ pub use self::zip::TrustedRandomAccessNoCoerce; #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::zip::zip; -/// This trait provides transitive access to source-stage in an iterator-adapter pipeline +#[unstable(feature = "iter_dedup", reason = "recently added", issue = "83748")] +pub use self::dedup::{Dedup, DedupBy, DedupByKey}; + +/// This trait provides transitive access to source-stage in an interator-adapter pipeline /// under the conditions that /// * the iterator source `S` itself implements `SourceIter` /// * there is a delegating implementation of this trait for each adapter in the pipeline between diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 9514466bd0c0..c0127683046f 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -423,6 +423,8 @@ pub use self::adapters::{ Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan, Skip, SkipWhile, Take, TakeWhile, Zip, }; +#[unstable(feature = "iter_dedup", reason = "recently added", issue = "83748")] +pub use self::adapters::{Dedup, DedupBy, DedupByKey}; #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] pub use self::adapters::{Intersperse, IntersperseWith}; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index b2d08f4b0f67..635998933728 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -6,6 +6,7 @@ use super::super::try_process; use super::super::ByRefSized; use super::super::TrustedRandomAccessNoCoerce; use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; +use super::super::{Dedup, DedupBy, DedupByKey}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ @@ -1689,6 +1690,142 @@ pub trait Iterator { Inspect::new(self, f) } + /// Removes all but the first of consecutive repeated elements in the iterator + /// according to the [`PartialEq`] trait implementation. + /// + /// For an iterator yielding infinitely many consecutive duplicates, + /// this may result in an infinite loop. + /// + /// If the iterator is sorted, this removes all duplicates. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_dedup)] + /// + /// let vec = vec![1, 2, 2, 3, 2]; + /// + /// let mut iter = vec.into_iter().dedup(); + /// + /// assert_eq!(iter.next(), Some(1)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(2)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// Example of an infinite loop: + /// + /// ```no_run + /// #![feature(iter_dedup)] + /// + /// // this will never terminate + /// let _ = std::iter::repeat(2).dedup().next(); + /// ``` + #[unstable(feature = "iter_dedup", reason = "recently added", issue = "83748")] + #[inline] + fn dedup(self) -> Dedup + where + Self: Sized, + Self::Item: PartialEq, + { + Dedup::new(self) + } + + /// Removes all but the first of consecutive elements in the iterator + /// satisfying a given equality relation. + /// + /// The `same_bucket` function is passed a references to two elements from + /// the iterator and must determine if the elements compare equal. + /// + /// For an iterator yielding infinitely many consecutive duplicates, + /// this may result in an infinite loop. + /// + /// If the iterator is sorted, this removes all duplicates. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_dedup)] + /// + /// let vec = vec!["foo", "bar", "Bar", "baz", "bar"]; + /// + /// let mut iter = vec.into_iter().dedup_by(|a, b| a.eq_ignore_ascii_case(b)); + /// + /// assert_eq!(iter.next(), Some("foo")); + /// assert_eq!(iter.next(), Some("bar")); + /// assert_eq!(iter.next(), Some("baz")); + /// assert_eq!(iter.next(), Some("bar")); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// Example of an infinite loop: + /// + /// ```no_run + /// #![feature(iter_dedup)] + /// + /// // this will never terminate + /// let _ = std::iter::repeat(2).dedup_by(|a, b| a == b).next(); + /// ``` + #[unstable(feature = "iter_dedup", reason = "recently added", issue = "83748")] + #[inline] + fn dedup_by(self, same_bucket: F) -> DedupBy + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> bool, + { + DedupBy::new(self, same_bucket) + } + + /// Removes all but the first of consecutive elements in the iterator + /// that resolve to the same key. + /// + /// For an iterator yielding infinitely many consecutive duplicates, + /// this may result in an infinite loop. + /// + /// If the iterator is sorted, this removes all duplicates. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_dedup)] + /// + /// let vec = vec![10, 20, 21, 30, 20]; + /// + /// let mut iter = vec.into_iter().dedup_by_key(|&i| i / 10); + /// + /// assert_eq!(iter.next(), Some(10)); + /// assert_eq!(iter.next(), Some(20)); + /// assert_eq!(iter.next(), Some(30)); + /// assert_eq!(iter.next(), Some(20)); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// Example of an infinite loop: + /// + /// ```no_run + /// #![feature(iter_dedup)] + /// + /// // this will never terminate + /// let _ = std::iter::repeat(2).dedup_by_key(|&n| n).next(); + /// ``` + #[unstable(feature = "iter_dedup", reason = "recently added", issue = "83748")] + #[inline] + fn dedup_by_key(self, key: F) -> DedupByKey + where + Self: Sized, + F: FnMut(&Self::Item) -> K, + K: PartialEq, + { + DedupByKey::new(self, key) + } + /// Borrows an iterator, rather than consuming it. /// /// This is useful to allow applying iterator adapters while still