1 | /// Converts an iterator of tuples into a tuple of containers. |
2 | /// |
3 | /// `multiunzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each |
4 | /// column. |
5 | /// |
6 | /// This function is, in some sense, the opposite of [`multizip`]. |
7 | /// |
8 | /// ``` |
9 | /// use itertools::multiunzip; |
10 | /// |
11 | /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; |
12 | /// |
13 | /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(inputs); |
14 | /// |
15 | /// assert_eq!(a, vec![1, 4, 7]); |
16 | /// assert_eq!(b, vec![2, 5, 8]); |
17 | /// assert_eq!(c, vec![3, 6, 9]); |
18 | /// ``` |
19 | /// |
20 | /// [`multizip`]: crate::multizip |
21 | pub fn multiunzip<FromI, I>(i: I) -> FromI |
22 | where |
23 | I: IntoIterator, |
24 | I::IntoIter: MultiUnzip<FromI>, |
25 | { |
26 | i.into_iter().multiunzip() |
27 | } |
28 | |
29 | /// An iterator that can be unzipped into multiple collections. |
30 | /// |
31 | /// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information. |
32 | pub trait MultiUnzip<FromI>: Iterator { |
33 | /// Unzip this iterator into multiple collections. |
34 | fn multiunzip(self) -> FromI; |
35 | } |
36 | |
37 | macro_rules! impl_unzip_iter { |
38 | ($($T:ident => $FromT:ident),*) => ( |
39 | #[allow(non_snake_case)] |
40 | impl<IT: Iterator<Item = ($($T,)*)>, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT { |
41 | fn multiunzip(self) -> ($($FromT,)*) { |
42 | // This implementation mirrors the logic of Iterator::unzip resp. Extend for (A, B) as close as possible. |
43 | // Unfortunately a lot of the used api there is still unstable (https://github.com/rust-lang/rust/issues/72631). |
44 | // |
45 | // Iterator::unzip: https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2825-2865 |
46 | // Extend for (A, B): https://doc.rust-lang.org/src/core/iter/traits/collect.rs.html#370-411 |
47 | |
48 | let mut res = ($($FromT::default(),)*); |
49 | let ($($FromT,)*) = &mut res; |
50 | |
51 | // Still unstable #72631 |
52 | // let (lower_bound, _) = self.size_hint(); |
53 | // if lower_bound > 0 { |
54 | // $($FromT.extend_reserve(lower_bound);)* |
55 | // } |
56 | |
57 | self.fold((), |(), ($($T,)*)| { |
58 | // Still unstable #72631 |
59 | // $( $FromT.extend_one($T); )* |
60 | $( $FromT.extend(std::iter::once($T)); )* |
61 | }); |
62 | res |
63 | } |
64 | } |
65 | ); |
66 | } |
67 | |
68 | impl_unzip_iter!(); |
69 | impl_unzip_iter!(A => FromA); |
70 | impl_unzip_iter!(A => FromA, B => FromB); |
71 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC); |
72 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD); |
73 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE); |
74 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF); |
75 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG); |
76 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH); |
77 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI); |
78 | impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ); |
79 | 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); |
80 | 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); |
81 | |