Skip to content

Commit

Permalink
Implement Itertools::multiunzip
Browse files Browse the repository at this point in the history
  • Loading branch information
Veykril committed Jul 24, 2021
1 parent 20c09bd commit aad1260
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/lib.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -3401,6 +3403,31 @@ pub trait Itertools : Iterator {
{
self.map(f).counts()
}

/// Unzips an iterator over tuples into a tuple of containers.
///
/// The first element of each tuple will be put into the first container, the second element into
/// the second, ....
///
/// It can be thought of as the reverse operation to [`Itertools::multiunzip`].
///
/// ```
/// 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, b, c), (vec![1, 4, 7], vec![2, 5, 8], 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 { }
Expand Down
58 changes: 58 additions & 0 deletions src/unziptuple.rs
@@ -0,0 +1,58 @@
/// Unzips an iterator over tuples into a tuple of containers.
///
/// ```
/// 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, b, c), (vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]));
/// ```
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),*) => (
impl_unzip_iter!(@rec $($T => $FromT,)*);
);
(@rec) => ();
(@rec $__:ident => $___:ident, $($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,)*) {
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!(@rec $($T => $FromT,)*);
);
}

impl_unzip_iter!(L => FromL, K => FromK, J => FromJ, I => FromI, H => FromH, G => FromG, F => FromF, E => FromE, D => FromD, C => FromC, B => FromB, A => FromA);
6 changes: 6 additions & 0 deletions tests/test_std.rs
Expand Up @@ -1080,3 +1080,9 @@ 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]));
}

0 comments on commit aad1260

Please sign in to comment.