aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 15:59:25 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-28 15:59:25 +0000
commitdc827c69e2060f747991536e10cf9951c91bbc51 (patch)
treed7d800f84b0d4ccd703059529235b632ba6bc92c
parentd04df4fff8360dc1121f66473dad7903a09d8201 (diff)
parente02c202c4051e7f57491ef1f85dc1ed8ba820b79 (diff)
downloaditertools-android13-frc-os-statsd-release.tar.gz
Snap for 8512216 from e02c202c4051e7f57491ef1f85dc1ed8ba820b79 to tm-frc-os-statsd-releaset_frc_sta_330443010android13-frc-os-statsd-release
Change-Id: I3b842d3586b9aa120afabc80fe39c02eaf671a89
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp2
-rw-r--r--CHANGELOG.md11
-rw-r--r--Cargo.toml3
-rw-r--r--Cargo.toml.orig3
-rw-r--r--METADATA10
-rw-r--r--README.md (renamed from README.rst)43
-rw-r--r--TEST_MAPPING6
-rw-r--r--src/adaptors/coalesce.rs8
-rw-r--r--src/adaptors/map.rs8
-rw-r--r--src/adaptors/mod.rs30
-rw-r--r--src/adaptors/multi_product.rs8
-rw-r--r--src/duplicates_impl.rs23
-rw-r--r--src/free.rs37
-rw-r--r--src/impl_macros.rs2
-rw-r--r--src/kmerge_impl.rs2
-rw-r--r--src/lib.rs79
-rw-r--r--src/multipeek_impl.rs1
-rw-r--r--src/pad_tail.rs7
-rw-r--r--src/peeking_take_while.rs7
-rw-r--r--src/process_results_impl.rs17
-rw-r--r--src/tuple_impl.rs4
-rw-r--r--src/unziptuple.rs80
-rw-r--r--tests/fold_specialization.rs13
-rw-r--r--tests/specializations.rs85
-rw-r--r--tests/test_core.rs19
-rw-r--r--tests/test_std.rs40
27 files changed, 464 insertions, 86 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index b29a8ee..5b491ea 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "20c09bd4fed037fa3df6f2a6a1e717b01be89f11"
+ "sha1": "3003c2a968f144cbccb63c768d2fec2e83a69ca4"
}
}
diff --git a/Android.bp b/Android.bp
index ba23bc5..09dfee1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,7 +43,7 @@ rust_library {
host_supported: true,
crate_name: "itertools",
cargo_env_compat: true,
- cargo_pkg_version: "0.10.1",
+ cargo_pkg_version: "0.10.3",
srcs: ["src/lib.rs"],
edition: "2018",
features: [
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41c4ab0..5e2032c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## 0.10.2
+ - Add `Itertools::multiunzip` (#362, #565)
+ - Add `intersperse` and `intersperse_with` free functions (#555)
+ - Add `Itertools::sorted_by_cached_key` (#424, #575)
+ - Specialize `ProcessResults::fold` (#563)
+ - Fix subtraction overflow in `DuplicatesBy::size_hint` (#552)
+ - Fix specialization tests (#574)
+ - More `Debug` impls (#573)
+ - Deprecate `fold1` (use `reduce` instead) (#580)
+ - Documentation fixes (`HomogenousTuple`, `into_group_map`, `into_group_map_by`, `MultiPeek::peek`) (#543 et al.)
+
## 0.10.1
- Add `Itertools::contains` (#514)
- Add `Itertools::counts_by` (#515)
diff --git a/Cargo.toml b/Cargo.toml
index fb84927..525cae5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,11 +13,12 @@
[package]
edition = "2018"
name = "itertools"
-version = "0.10.1"
+version = "0.10.3"
authors = ["bluss"]
exclude = ["/bors.toml"]
description = "Extra iterator adaptors, iterator methods, free functions, and macros."
documentation = "https://docs.rs/itertools/"
+readme = "README.md"
keywords = ["iterator", "data-structure", "zip", "product", "group-by"]
categories = ["algorithms", "rust-patterns"]
license = "MIT/Apache-2.0"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index d7f6b23..22a08a8 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,11 +1,12 @@
[package]
name = "itertools"
-version = "0.10.1"
+version = "0.10.3"
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-itertools/itertools"
documentation = "https://docs.rs/itertools/"
authors = ["bluss"]
+readme = "README.md"
description = "Extra iterator adaptors, iterator methods, free functions, and macros."
diff --git a/METADATA b/METADATA
index ce650b4..ca38654 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/itertools/itertools-0.10.1.crate"
+ value: "https://static.crates.io/crates/itertools/itertools-0.10.3.crate"
}
- version: "0.10.1"
+ version: "0.10.3"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 6
- day: 21
+ year: 2022
+ month: 3
+ day: 1
}
}
diff --git a/README.rst b/README.md
index aa37f6b..4cc3f8f 100644
--- a/README.rst
+++ b/README.md
@@ -1,50 +1,39 @@
-
-Itertools
-=========
+# Itertools
Extra iterator adaptors, functions and macros.
-Please read the `API documentation here`__
-
-__ https://docs.rs/itertools/
-
-|build_status|_ |crates|_
+Please read the [API documentation here](https://docs.rs/itertools/).
-.. |build_status| image:: https://travis-ci.org/rust-itertools/itertools.svg?branch=master
-.. _build_status: https://travis-ci.org/rust-itertools/itertools
+[![build_status](https://github.com/rust-itertools/itertools/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-itertools/itertools/actions)
+[![crates.io](https://img.shields.io/crates/v/itertools.svg)](https://crates.io/crates/itertools)
-.. |crates| image:: https://meritbadge.herokuapp.com/itertools
-.. _crates: https://crates.io/crates/itertools
+How to use with Cargo:
-How to use with cargo:
-
-.. code:: toml
-
- [dependencies]
- itertools = "0.10.0"
+```toml
+[dependencies]
+itertools = "0.10.2"
+```
How to use in your crate:
-.. code:: rust
-
- use itertools::Itertools;
+```rust
+use itertools::Itertools;
+```
-How to contribute
------------------
+## How to contribute
- Fix a bug or implement a new thing
-- Include tests for your new feature, preferably a quickcheck test
+- Include tests for your new feature, preferably a QuickCheck test
- Make a Pull Request
-For new features, please first consider filing a PR to `rust-lang/rust <https://github.com/rust-lang/rust>`_,
+For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust),
adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable.
If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea.
The reason for doing is this is so that we avoid future breakage as with ``.flatten()``.
However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``,
then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead.
-License
--------
+## License
Dual-licensed to be compatible with the Rust project.
diff --git a/TEST_MAPPING b/TEST_MAPPING
index f6e148f..86d1024 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -17,11 +17,17 @@
"presubmit": [
{
"name": "apkdmverity.test"
+ },
+ {
+ "name": "microdroid_manager_test"
}
],
"presubmit-rust": [
{
"name": "apkdmverity.test"
+ },
+ {
+ "name": "microdroid_manager_test"
}
]
}
diff --git a/src/adaptors/coalesce.rs b/src/adaptors/coalesce.rs
index 1afbee5..b1aff6e 100644
--- a/src/adaptors/coalesce.rs
+++ b/src/adaptors/coalesce.rs
@@ -119,6 +119,10 @@ pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as It
#[derive(Clone)]
pub struct DedupPred2CoalescePred<DP>(DP);
+impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> {
+ debug_fmt_fields!(DedupPred2CoalescePred,);
+}
+
pub trait DedupPredicate<T> {
// TODO replace by Fn(&T, &T)->bool once Rust supports it
fn dedup_pair(&mut self, a: &T, b: &T) -> bool;
@@ -137,7 +141,7 @@ where
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct DedupEq;
impl<T: PartialEq> DedupPredicate<T> for DedupEq {
@@ -186,7 +190,7 @@ where
pub type DedupByWithCount<I, Pred> =
CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>;
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct DedupPredWithCount2CoalescePred<DP>(DP);
impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP>
diff --git a/src/adaptors/map.rs b/src/adaptors/map.rs
index eee5cc3..cf5e5a0 100644
--- a/src/adaptors/map.rs
+++ b/src/adaptors/map.rs
@@ -1,7 +1,7 @@
use std::iter::FromIterator;
use std::marker::PhantomData;
-#[derive(Clone)]
+#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapSpecialCase<I, F> {
iter: I,
@@ -84,6 +84,10 @@ where
#[derive(Clone)]
pub struct MapSpecialCaseFnOk<F>(F);
+impl<F> std::fmt::Debug for MapSpecialCaseFnOk<F> {
+ debug_fmt_fields!(MapSpecialCaseFnOk,);
+}
+
/// Create a new `MapOk` iterator.
pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F>
where
@@ -108,7 +112,7 @@ impl<T: Into<U>, U> MapSpecialCaseFn<T> for MapSpecialCaseFnInto<U> {
}
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct MapSpecialCaseFnInto<U>(PhantomData<U>);
/// Create a new [`MapInto`] iterator.
diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs
index bd0d93a..2010f53 100644
--- a/src/adaptors/mod.rs
+++ b/src/adaptors/mod.rs
@@ -477,7 +477,7 @@ pub trait MergePredicate<T> {
fn merge_pred(&mut self, a: &T, b: &T) -> bool;
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct MergeLte;
impl<T: PartialOrd> MergePredicate<T> for MergeLte {
@@ -849,6 +849,13 @@ pub struct FilterOk<I, F> {
f: F
}
+impl<I, F> fmt::Debug for FilterOk<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(FilterOk, iter);
+}
+
/// Create a new `FilterOk` iterator.
pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F>
where I: Iterator<Item = Result<T, E>>,
@@ -917,6 +924,13 @@ pub struct FilterMapOk<I, F> {
f: F
}
+impl<I, F> fmt::Debug for FilterMapOk<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(FilterMapOk, iter);
+}
+
fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> {
match result {
Ok(Some(v)) => Some(Ok(v)),
@@ -995,6 +1009,13 @@ pub struct Positions<I, F> {
count: usize,
}
+impl<I, F> fmt::Debug for Positions<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(Positions, iter, count);
+}
+
/// Create a new `Positions` iterator.
pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
where I: Iterator,
@@ -1058,6 +1079,13 @@ pub struct Update<I, F> {
f: F,
}
+impl<I, F> fmt::Debug for Update<I, F>
+where
+ I: fmt::Debug,
+{
+ debug_fmt_fields!(Update, iter);
+}
+
/// Create a new `Update` iterator.
pub fn update<I, F>(iter: I, f: F) -> Update<I, F>
where
diff --git a/src/adaptors/multi_product.rs b/src/adaptors/multi_product.rs
index 9708ef4..30650ed 100644
--- a/src/adaptors/multi_product.rs
+++ b/src/adaptors/multi_product.rs
@@ -18,6 +18,14 @@ pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
where I: Iterator + Clone,
I::Item: Clone;
+impl<I> std::fmt::Debug for MultiProduct<I>
+where
+ I: Iterator + Clone + std::fmt::Debug,
+ I::Item: Clone + std::fmt::Debug,
+{
+ debug_fmt_fields!(CoalesceBy, 0);
+}
+
/// Create a new cartesian product iterator over an arbitrary number
/// of iterators of the same type.
///
diff --git a/src/duplicates_impl.rs b/src/duplicates_impl.rs
index 42049df..640d481 100644
--- a/src/duplicates_impl.rs
+++ b/src/duplicates_impl.rs
@@ -84,13 +84,18 @@ mod private {
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, hi) = self.iter.size_hint();
- // There are `hi` number of items left in the base iterator. In the best case scenario,
- // these items are exactly the same as the ones pending (i.e items seen exactly once so
- // far), plus (hi - pending) / 2 pairs of never seen before items.
let hi = hi.map(|hi| {
- let max_pending = std::cmp::min(self.meta.pending, hi);
- let max_new = std::cmp::max(hi - self.meta.pending, 0) / 2;
- max_pending + max_new
+ if hi <= self.meta.pending {
+ // fewer or equally many iter-remaining elements than pending elements
+ // => at most, each iter-remaining element is matched
+ hi
+ } else {
+ // fewer pending elements than iter-remaining elements
+ // => at most:
+ // * each pending element is matched
+ // * the other iter-remaining elements come in pairs
+ self.meta.pending + (hi - self.meta.pending) / 2
+ }
});
// The lower bound is always 0 since we might only get unique items from now on
(0, hi)
@@ -117,6 +122,7 @@ mod private {
}
/// Apply the identity function to elements before checking them for equality.
+ #[derive(Debug)]
pub struct ById;
impl<V> KeyMethod<V, V> for ById {
type Container = JustValue<V>;
@@ -128,6 +134,9 @@ mod private {
/// Apply a user-supplied function to elements before checking them for equality.
pub struct ByFn<F>(pub(crate) F);
+ impl<F> fmt::Debug for ByFn<F> {
+ debug_fmt_fields!(ByFn,);
+ }
impl<K, V, F> KeyMethod<K, V> for ByFn<F>
where
F: FnMut(&V) -> K,
@@ -147,6 +156,7 @@ mod private {
fn value(self) -> V;
}
+ #[derive(Debug)]
pub struct KeyValue<K, V>(K, V);
impl<K, V> KeyXorValue<K, V> for KeyValue<K, V> {
fn key_ref(&self) -> &K {
@@ -160,6 +170,7 @@ mod private {
}
}
+ #[derive(Debug)]
pub struct JustValue<V>(V);
impl<V> KeyXorValue<V, V> for JustValue<V> {
fn key_ref(&self) -> &V {
diff --git a/src/free.rs b/src/free.rs
index c78dc1d..6674030 100644
--- a/src/free.rs
+++ b/src/free.rs
@@ -14,8 +14,8 @@ use alloc::{
string::String,
};
-#[cfg(feature = "use_alloc")]
use crate::Itertools;
+use crate::intersperse::{Intersperse, IntersperseWith};
pub use crate::adaptors::{
interleave,
@@ -35,6 +35,41 @@ pub use crate::merge_join::merge_join_by;
#[cfg(feature = "use_alloc")]
pub use crate::rciter_impl::rciter;
+/// Iterate `iterable` with a particular value inserted between each element.
+///
+/// [`IntoIterator`] enabled version of [`Iterator::intersperse`].
+///
+/// ```
+/// use itertools::intersperse;
+///
+/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]);
+/// ```
+pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter>
+ where I: IntoIterator,
+ <I as IntoIterator>::Item: Clone
+{
+ Itertools::intersperse(iterable.into_iter(), element)
+}
+
+/// Iterate `iterable` with a particular value created by a function inserted
+/// between each element.
+///
+/// [`IntoIterator`] enabled version of [`Iterator::intersperse_with`].
+///
+/// ```
+/// use itertools::intersperse_with;
+///
+/// let mut i = 10;
+/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]);
+/// assert_eq!(i, 8);
+/// ```
+pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F>
+ where I: IntoIterator,
+ F: FnMut() -> I::Item
+{
+ Itertools::intersperse_with(iterable.into_iter(), element)
+}
+
/// Iterate `iterable` with a running index.
///
/// [`IntoIterator`] enabled version of [`Iterator::enumerate`].
diff --git a/src/impl_macros.rs b/src/impl_macros.rs
index 3da8c63..5772bae 100644
--- a/src/impl_macros.rs
+++ b/src/impl_macros.rs
@@ -2,7 +2,7 @@
//! Implementation's internal macros
macro_rules! debug_fmt_fields {
- ($tyname:ident, $($($field:ident).+),*) => {
+ ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct(stringify!($tyname))
$(
diff --git a/src/kmerge_impl.rs b/src/kmerge_impl.rs
index dce5b78..bd56b03 100644
--- a/src/kmerge_impl.rs
+++ b/src/kmerge_impl.rs
@@ -111,7 +111,7 @@ pub trait KMergePredicate<T> {
fn kmerge_pred(&mut self, a: &T, b: &T) -> bool;
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct KMergeByLt;
impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt {
diff --git a/src/lib.rs b/src/lib.rs
index 7dd6241..df95e19 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -179,6 +179,7 @@ pub use crate::repeatn::repeat_n;
#[allow(deprecated)]
pub use crate::sources::{repeat_call, unfold, iterate};
pub use crate::with_position::Position;
+pub use crate::unziptuple::{multiunzip, MultiUnzip};
pub use crate::ziptuple::multizip;
mod adaptors;
mod either_or_both;
@@ -237,6 +238,7 @@ mod tuple_impl;
mod duplicates_impl;
#[cfg(feature = "use_std")]
mod unique_impl;
+mod unziptuple;
mod with_position;
mod zip_eq_impl;
mod zip_longest;
@@ -660,7 +662,7 @@ pub trait Itertools : Iterator {
}
/// Return an iterator over all contiguous windows producing tuples of
- /// a specific size (up to 4).
+ /// a specific size (up to 12).
///
/// `tuple_windows` clones the iterator elements so that they can be
/// part of successive windows, this makes it most suited for iterators
@@ -702,7 +704,7 @@ pub trait Itertools : Iterator {
/// Return an iterator over all windows, wrapping back to the first
/// elements when the window would otherwise exceed the length of the
- /// iterator, producing tuples of a specific size (up to 4).
+ /// iterator, producing tuples of a specific size (up to 12).
///
/// `circular_tuple_windows` clones the iterator elements so that they can be
/// part of successive windows, this makes it most suited for iterators
@@ -735,7 +737,7 @@ pub trait Itertools : Iterator {
tuple_impl::circular_tuple_windows(self)
}
/// Return an iterator that groups the items in tuples of a specific size
- /// (up to 4).
+ /// (up to 12).
///
/// See also the method [`.next_tuple()`](Itertools::next_tuple).
///
@@ -2241,6 +2243,7 @@ pub trait Itertools : Iterator {
/// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45);
/// assert_eq!((0..0).fold1(|x, y| x * y), None);
/// ```
+ #[deprecated(since = "0.10.2", note = "Use `Iterator::reduce` instead")]
fn fold1<F>(mut self, f: F) -> Option<Self::Item>
where F: FnMut(Self::Item, Self::Item) -> Self::Item,
Self: Sized,
@@ -2665,6 +2668,43 @@ pub trait Itertools : Iterator {
v.into_iter()
}
+ /// Sort all iterator elements into a new iterator in ascending order. The key function is
+ /// called exactly once per key.
+ ///
+ /// **Note:** This consumes the entire iterator, uses the
+ /// [`slice::sort_by_cached_key`] method and returns the result as a new
+ /// iterator that owns its elements.
+ ///
+ /// The sorted iterator, if directly collected to a `Vec`, is converted
+ /// without any extra copying or allocation cost.
+ ///
+ /// ```
+ /// use itertools::Itertools;
+ ///
+ /// // sort people in descending order by age
+ /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)];
+ ///
+ /// let oldest_people_first = people
+ /// .into_iter()
+ /// .sorted_by_cached_key(|x| -x.1)
+ /// .map(|(person, _age)| person);
+ ///
+ /// itertools::assert_equal(oldest_people_first,
+ /// vec!["Jill", "Jack", "Jane", "John"]);
+ /// ```
+ /// ```
+ #[cfg(feature = "use_alloc")]
+ fn sorted_by_cached_key<K, F>(self, f: F) -> VecIntoIter<Self::Item>
+ where
+ Self: Sized,
+ K: Ord,
+ F: FnMut(&Self::Item) -> K,
+ {
+ let mut v = Vec::from_iter(self);
+ v.sort_by_cached_key(f);
+ v.into_iter()
+ }
+
/// Sort the k smallest elements into a new iterator, in ascending order.
///
/// **Note:** This consumes the entire iterator, and returns the result
@@ -2770,6 +2810,8 @@ pub trait Itertools : Iterator {
/// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values
/// are taken from `(Key, Value)` tuple pairs yielded by the input iterator.
///
+ /// Essentially a shorthand for `.into_grouping_map().collect::<Vec<_>>()`.
+ ///
/// ```
/// use itertools::Itertools;
///
@@ -2791,8 +2833,8 @@ pub trait Itertools : Iterator {
/// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified
/// in the closure.
- /// Different to `into_group_map_by` because the key is still present. It is also more general.
- /// You can also fold the `group_map`.
+ ///
+ /// Essentially a shorthand for `.into_grouping_map_by(f).collect::<Vec<_>>()`.
///
/// ```
/// use itertools::Itertools;
@@ -3401,6 +3443,33 @@ pub trait Itertools : Iterator {
{
self.map(f).counts()
}
+
+ /// Converts an iterator of tuples into a tuple of containers.
+ ///
+ /// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
+ /// column.
+ ///
+ /// This function is, in some sense, the opposite of [`multizip`].
+ ///
+ /// ```
+ /// use itertools::Itertools;
+ ///
+ /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
+ ///
+ /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = inputs
+ /// .into_iter()
+ /// .multiunzip();
+ ///
+ /// assert_eq!(a, vec![1, 4, 7]);
+ /// assert_eq!(b, vec![2, 5, 8]);
+ /// assert_eq!(c, vec![3, 6, 9]);
+ /// ```
+ fn multiunzip<FromI>(self) -> FromI
+ where
+ Self: Sized + MultiUnzip<FromI>,
+ {
+ MultiUnzip::multiunzip(self)
+ }
}
impl<T: ?Sized> Itertools for T where T: Iterator { }
diff --git a/src/multipeek_impl.rs b/src/multipeek_impl.rs
index 93b0227..5917681 100644
--- a/src/multipeek_impl.rs
+++ b/src/multipeek_impl.rs
@@ -38,6 +38,7 @@ impl<I: Iterator> MultiPeek<I> {
/// Works exactly like `.next()` with the only difference that it doesn't
/// advance itself. `.peek()` can be called multiple times, to peek
/// further ahead.
+ /// When `.next()` is called, reset the peeking “cursor”.
pub fn peek(&mut self) -> Option<&I::Item> {
let ret = if self.index < self.buf.len() {
Some(&self.buf[self.index])
diff --git a/src/pad_tail.rs b/src/pad_tail.rs
index 03867cb..de57ee4 100644
--- a/src/pad_tail.rs
+++ b/src/pad_tail.rs
@@ -16,6 +16,13 @@ pub struct PadUsing<I, F> {
filler: F,
}
+impl<I, F> std::fmt::Debug for PadUsing<I, F>
+where
+ I: std::fmt::Debug,
+{
+ debug_fmt_fields!(PadUsing, iter, min, pos);
+}
+
/// Create a new **PadUsing** iterator.
pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F>
where I: Iterator,
diff --git a/src/peeking_take_while.rs b/src/peeking_take_while.rs
index f9f2134..cd0945a 100644
--- a/src/peeking_take_while.rs
+++ b/src/peeking_take_while.rs
@@ -83,6 +83,13 @@ pub struct PeekingTakeWhile<'a, I: 'a, F>
f: F,
}
+impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F>
+where
+ I: Iterator + std::fmt::Debug,
+{
+ debug_fmt_fields!(PeekingTakeWhile, iter);
+}
+
/// Create a PeekingTakeWhile
pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F>
where I: Iterator,
diff --git a/src/process_results_impl.rs b/src/process_results_impl.rs
index 9da108b..44308f3 100644
--- a/src/process_results_impl.rs
+++ b/src/process_results_impl.rs
@@ -30,6 +30,23 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.iter.size_hint().1)
}
+
+ fn fold<B, F>(mut self, init: B, mut f: F) -> B
+ where
+ Self: Sized,
+ F: FnMut(B, Self::Item) -> B,
+ {
+ let error = self.error;
+ self.iter
+ .try_fold(init, |acc, opt| match opt {
+ Ok(x) => Ok(f(acc, x)),
+ Err(e) => {
+ *error = Err(e);
+ Err(acc)
+ }
+ })
+ .unwrap_or_else(|e| e)
+ }
}
/// “Lift” a function of the values of an iterator so that it can process
diff --git a/src/tuple_impl.rs b/src/tuple_impl.rs
index ca8b97c..d914e03 100644
--- a/src/tuple_impl.rs
+++ b/src/tuple_impl.rs
@@ -11,7 +11,7 @@ use std::marker::PhantomData;
// hiding the implementation details of `TupleCollect`.
// See https://github.com/rust-itertools/itertools/issues/387
-/// Implemented for homogeneous tuples of size up to 4.
+/// Implemented for homogeneous tuples of size up to 12.
pub trait HomogeneousTuple
: TupleCollect
{}
@@ -77,7 +77,7 @@ impl<T> ExactSizeIterator for TupleBuffer<T>
/// An iterator that groups the items in tuples of a specific size.
///
/// See [`.tuples()`](crate::Itertools::tuples) for more information.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct Tuples<I, T>
where I: Iterator<Item = T::Item>,
diff --git a/src/unziptuple.rs b/src/unziptuple.rs
new file mode 100644
index 0000000..f468f05
--- /dev/null
+++ b/src/unziptuple.rs
@@ -0,0 +1,80 @@
+/// Converts an iterator of tuples into a tuple of containers.
+///
+/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each
+/// column.
+///
+/// This function is, in some sense, the opposite of [`multizip`].
+///
+/// ```
+/// use itertools::multiunzip;
+///
+/// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)];
+///
+/// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(inputs);
+///
+/// assert_eq!(a, vec![1, 4, 7]);
+/// assert_eq!(b, vec![2, 5, 8]);
+/// assert_eq!(c, vec![3, 6, 9]);
+/// ```
+///
+/// [`multizip`]: crate::multizip
+pub fn multiunzip<FromI, I>(i: I) -> FromI
+where
+ I: IntoIterator,
+ I::IntoIter: MultiUnzip<FromI>,
+{
+ i.into_iter().multiunzip()
+}
+
+/// An iterator that can be unzipped into multiple collections.
+///
+/// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information.
+pub trait MultiUnzip<FromI>: Iterator {
+ /// Unzip this iterator into multiple collections.
+ fn multiunzip(self) -> FromI;
+}
+
+macro_rules! impl_unzip_iter {
+ ($($T:ident => $FromT:ident),*) => (
+ #[allow(non_snake_case)]
+ impl<IT: Iterator<Item = ($($T,)*)>, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT {
+ fn multiunzip(self) -> ($($FromT,)*) {
+ // This implementation mirrors the logic of Iterator::unzip as close as possible.
+ // Unfortunately a lot of the used api there is still unstable represented by
+ // the commented out parts that follow.
+ //
+ // https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2816-2844
+
+ let mut res = ($($FromT::default(),)*);
+ let ($($FromT,)*) = &mut res;
+
+ // Still unstable #72631
+ // let (lower_bound, _) = self.size_hint();
+ // if lower_bound > 0 {
+ // $($FromT.extend_reserve(lower_bound);)*
+ // }
+
+ self.fold((), |(), ($($T,)*)| {
+ // Still unstable #72631
+ // $( $FromT.extend_one($T); )*
+ $( $FromT.extend(std::iter::once($T)); )*
+ });
+ res
+ }
+ }
+ );
+}
+
+impl_unzip_iter!();
+impl_unzip_iter!(A => FromA);
+impl_unzip_iter!(A => FromA, B => FromB);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK);
+impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK, L => FromL);
diff --git a/tests/fold_specialization.rs b/tests/fold_specialization.rs
deleted file mode 100644
index a984b40..0000000
--- a/tests/fold_specialization.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use itertools::Itertools;
-
-#[test]
-fn specialization_intersperse() {
- let mut iter = (1..2).intersperse(0);
- iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
-
- let mut iter = (1..3).intersperse(0);
- iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
-
- let mut iter = (1..4).intersperse(0);
- iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
-}
diff --git a/tests/specializations.rs b/tests/specializations.rs
index bc337c2..199cf56 100644
--- a/tests/specializations.rs
+++ b/tests/specializations.rs
@@ -15,16 +15,16 @@ where
}
}
-fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F)
-where
- V: Eq + Debug,
- Iter: Iterator<Item = IterItem> + Clone + 'a,
- F: Fn(Box<dyn Iterator<Item = IterItem> + 'a>) -> V,
-{
- assert_eq!(
- mapper(Box::new(Unspecialized(iterator.clone()))),
- mapper(Box::new(iterator.clone()))
- )
+macro_rules! check_specialized {
+ ($src:expr, |$it:pat| $closure:expr) => {
+ let $it = $src.clone();
+ let v1 = $closure;
+
+ let $it = Unspecialized($src.clone());
+ let v2 = $closure;
+
+ assert_eq!(v1, v2);
+ }
}
fn test_specializations<IterItem, Iter>(
@@ -33,10 +33,10 @@ fn test_specializations<IterItem, Iter>(
IterItem: Eq + Debug + Clone,
Iter: Iterator<Item = IterItem> + Clone,
{
- check_specialized(it, |i| i.count());
- check_specialized(it, |i| i.last());
- check_specialized(it, |i| i.collect::<Vec<_>>());
- check_specialized(it, |i| {
+ check_specialized!(it, |i| i.count());
+ check_specialized!(it, |i| i.last());
+ check_specialized!(it, |i| i.collect::<Vec<_>>());
+ check_specialized!(it, |i| {
let mut parameters_from_fold = vec![];
let fold_result = i.fold(vec![], |mut acc, v: IterItem| {
parameters_from_fold.push((acc.clone(), v.clone()));
@@ -45,7 +45,7 @@ fn test_specializations<IterItem, Iter>(
});
(parameters_from_fold, fold_result)
});
- check_specialized(it, |mut i| {
+ check_specialized!(it, |mut i| {
let mut parameters_from_all = vec![];
let first = i.next();
let all_result = i.all(|x| {
@@ -56,7 +56,7 @@ fn test_specializations<IterItem, Iter>(
});
let size = it.clone().count();
for n in 0..size + 2 {
- check_specialized(it, |mut i| i.nth(n));
+ check_specialized!(it, |mut i| i.nth(n));
}
// size_hint is a bit harder to check
let mut it_sh = it.clone();
@@ -73,6 +73,12 @@ fn test_specializations<IterItem, Iter>(
}
quickcheck! {
+ fn intersperse(v: Vec<u8>) -> () {
+ test_specializations(&v.into_iter().intersperse(0));
+ }
+}
+
+quickcheck! {
fn put_back_qc(test_vec: Vec<i32>) -> () {
test_specializations(&itertools::put_back(test_vec.iter()));
let mut pb = itertools::put_back(test_vec.into_iter());
@@ -98,3 +104,50 @@ quickcheck! {
test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1)));
}
}
+
+quickcheck! {
+ fn process_results(v: Vec<Result<u8, u8>>) -> () {
+ helper(v.iter().copied());
+ helper(v.iter().copied().filter(Result::is_ok));
+
+ fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) {
+ macro_rules! check_results_specialized {
+ ($src:expr, |$it:pat| $closure:expr) => {
+ assert_eq!(
+ itertools::process_results($src.clone(), |$it| $closure),
+ itertools::process_results($src.clone(), |i| {
+ let $it = Unspecialized(i);
+ $closure
+ }),
+ )
+ }
+ }
+
+ check_results_specialized!(it, |i| i.count());
+ check_results_specialized!(it, |i| i.last());
+ check_results_specialized!(it, |i| i.collect::<Vec<_>>());
+ check_results_specialized!(it, |i| {
+ let mut parameters_from_fold = vec![];
+ let fold_result = i.fold(vec![], |mut acc, v| {
+ parameters_from_fold.push((acc.clone(), v.clone()));
+ acc.push(v);
+ acc
+ });
+ (parameters_from_fold, fold_result)
+ });
+ check_results_specialized!(it, |mut i| {
+ let mut parameters_from_all = vec![];
+ let first = i.next();
+ let all_result = i.all(|x| {
+ parameters_from_all.push(x.clone());
+ Some(x)==first
+ });
+ (parameters_from_all, all_result)
+ });
+ let size = it.clone().count();
+ for n in 0..size + 2 {
+ check_results_specialized!(it, |mut i| i.nth(n));
+ }
+ }
+ }
+}
diff --git a/tests/test_core.rs b/tests/test_core.rs
index bcdca0e..a7b7449 100644
--- a/tests/test_core.rs
+++ b/tests/test_core.rs
@@ -9,6 +9,8 @@ use core::iter;
use itertools as it;
use crate::it::Itertools;
use crate::it::interleave;
+use crate::it::intersperse;
+use crate::it::intersperse_with;
use crate::it::multizip;
use crate::it::free::put_back;
use crate::it::iproduct;
@@ -136,6 +138,23 @@ fn test_interleave() {
it::assert_equal(it, rs.iter());
}
+#[test]
+fn test_intersperse() {
+ let xs = [1u8, 2, 3];
+ let ys = [1u8, 0, 2, 0, 3];
+ let it = intersperse(&xs, &0);
+ it::assert_equal(it, ys.iter());
+}
+
+#[test]
+fn test_intersperse_with() {
+ let xs = [1u8, 2, 3];
+ let ys = [1u8, 10, 2, 10, 3];
+ let i = 10;
+ let it = intersperse_with(&xs, || &i);
+ it::assert_equal(it, ys.iter());
+}
+
#[allow(deprecated)]
#[test]
fn foreach() {
diff --git a/tests/test_std.rs b/tests/test_std.rs
index 7cda9b5..2049d15 100644
--- a/tests/test_std.rs
+++ b/tests/test_std.rs
@@ -84,6 +84,13 @@ fn duplicates() {
it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev());
let ys_rev = [1, 0];
it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev());
+
+ let xs = vec![0, 1, 2, 1, 2];
+ let ys = vec![1, 2];
+ assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec());
+ assert_eq!(ys, xs.iter().rev().duplicates().rev().cloned().collect_vec());
+ let ys_rev = vec![2, 1];
+ assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec());
}
#[test]
@@ -504,6 +511,30 @@ fn sorted_by_key() {
}
#[test]
+fn sorted_by_cached_key() {
+ // Track calls to key function
+ let mut ncalls = 0;
+
+ let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| {
+ ncalls += 1;
+ x.to_string()
+ });
+ it::assert_equal(sorted, vec![1, 2, 3, 4]);
+ // Check key function called once per element
+ assert_eq!(ncalls, 4);
+
+ let mut ncalls = 0;
+
+ let sorted = (0..5).sorted_by_cached_key(|&x| {
+ ncalls += 1;
+ -x
+ });
+ it::assert_equal(sorted, vec![4, 3, 2, 1, 0]);
+ // Check key function called once per element
+ assert_eq!(ncalls, 5);
+}
+
+#[test]
fn test_multipeek() {
let nums = vec![1u8,2,3,4,5];
@@ -1080,3 +1111,12 @@ fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice::
[].iter().exactly_one()?;
Ok(())
}
+
+#[test]
+fn multiunzip() {
+ let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)].iter().cloned().multiunzip();
+ assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8]));
+ let (): () = [(), (), ()].iter().cloned().multiunzip();
+ let t: (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)].iter().cloned().multiunzip();
+ assert_eq!(t, (vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11]));
+} \ No newline at end of file