Skip to content

Commit 821f623

Browse files
author
root
committed
Add ZipLongest adaptor by Simon Sapin, method is .zip_longest()
ZipLongest originally written by SimonSapin, and dedicated to itertools rust-lang/rust#19283
1 parent 2512606 commit 821f623

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,14 @@ pub use stride::StrideMut;
3939
pub use times::Times;
4040
pub use times::times;
4141
pub use linspace::{linspace, Linspace};
42+
pub use zip::{ZipLongest, EitherOrBoth};
4243
mod adaptors;
4344
mod intersperse;
4445
mod linspace;
4546
mod map;
4647
mod stride;
4748
mod times;
49+
mod zip;
4850

4951
/// A helper trait for (x,y,z) ++ w => (x,y,z,w),
5052
/// used for implementing `iproduct!` and `izip!`
@@ -243,6 +245,30 @@ pub trait Itertools<A> : Iterator<A> {
243245
Intersperse::new(self, element)
244246
}
245247

248+
/// Creates an iterator which iterates over both this and the specified
249+
/// iterators simultaneously, yielding pairs of two optional elements.
250+
/// When both iterators return None, all further invocations of next() will
251+
/// return None.
252+
///
253+
/// # Example
254+
///
255+
/// ```rust
256+
/// # use itertools::EitherOrBoth::{Both, Right};
257+
/// # use itertools::Itertools;
258+
/// let a = [0i];
259+
/// let b = [1i, 2i];
260+
/// let mut it = a.iter().cloned().zip_longest(b.iter().cloned());
261+
/// assert_eq!(it.next(), Some(Both(0, 1)));
262+
/// assert_eq!(it.next(), Some(Right(2)));
263+
/// assert_eq!(it.next(), None);
264+
/// ```
265+
///
266+
/// Iterator element type is `EitherOrBoth<A, B>`.
267+
#[inline]
268+
fn zip_longest<B, U: Iterator<B>>(self, other: U) -> ZipLongest<Self, U> {
269+
ZipLongest::new(self, other)
270+
}
271+
246272
// non-adaptor methods
247273

248274
/// Consume `n` elements of the iterator eagerly

src/zip.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::cmp;
2+
use self::EitherOrBoth::{Right, Left, Both};
3+
4+
// ZipLongest originally written by SimonSapin,
5+
// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
6+
7+
/// An iterator which iterates two other iterators simultaneously
8+
#[deriving(Clone)]
9+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
10+
pub struct ZipLongest<T, U> {
11+
a: T,
12+
b: U
13+
}
14+
15+
impl<T, U> ZipLongest<T, U>
16+
{
17+
/// Create a new ZipLongest iterator.
18+
pub fn new(a: T, b: U) -> ZipLongest<T, U>
19+
{
20+
ZipLongest{a: a, b: b}
21+
}
22+
}
23+
24+
impl<A, B, T: Iterator<A>, U: Iterator<B>> Iterator<EitherOrBoth<A, B>> for ZipLongest<T, U> {
25+
#[inline]
26+
fn next(&mut self) -> Option<EitherOrBoth<A, B>> {
27+
match (self.a.next(), self.b.next()) {
28+
(None, None) => None,
29+
(Some(a), None) => Some(Left(a)),
30+
(None, Some(b)) => Some(Right(b)),
31+
(Some(a), Some(b)) => Some(Both(a, b)),
32+
}
33+
}
34+
35+
#[inline]
36+
fn size_hint(&self) -> (uint, Option<uint>) {
37+
let (a_lower, a_upper) = self.a.size_hint();
38+
let (b_lower, b_upper) = self.b.size_hint();
39+
40+
let lower = cmp::max(a_lower, b_lower);
41+
42+
let upper = match (a_upper, b_upper) {
43+
(Some(x), Some(y)) => Some(cmp::max(x,y)),
44+
_ => None
45+
};
46+
47+
(lower, upper)
48+
}
49+
}
50+
51+
impl<A, B, T: ExactSizeIterator<A>, U: ExactSizeIterator<B>> DoubleEndedIterator<EitherOrBoth<A, B>>
52+
for ZipLongest<T, U> {
53+
#[inline]
54+
fn next_back(&mut self) -> Option<EitherOrBoth<A, B>> {
55+
use std::cmp::{Equal, Greater, Less};
56+
match self.a.len().cmp(&self.b.len()) {
57+
Equal => match (self.a.next_back(), self.b.next_back()) {
58+
(None, None) => None,
59+
(Some(a), Some(b)) => Some(Both(a, b)),
60+
// These can only happen if .len() is inconsistent with .next_back()
61+
(Some(a), None) => Some(Left(a)),
62+
(None, Some(b)) => Some(Right(b)),
63+
},
64+
Greater => self.a.next_back().map(Left),
65+
Less => self.b.next_back().map(Right),
66+
}
67+
}
68+
}
69+
70+
impl<A, B, T: RandomAccessIterator<A>, U: RandomAccessIterator<B>>
71+
RandomAccessIterator<EitherOrBoth<A, B>> for ZipLongest<T, U> {
72+
#[inline]
73+
fn indexable(&self) -> uint {
74+
cmp::max(self.a.indexable(), self.b.indexable())
75+
}
76+
77+
#[inline]
78+
fn idx(&mut self, index: uint) -> Option<EitherOrBoth<A, B>> {
79+
match (self.a.idx(index), self.b.idx(index)) {
80+
(None, None) => None,
81+
(Some(a), None) => Some(Left(a)),
82+
(None, Some(b)) => Some(Right(b)),
83+
(Some(a), Some(b)) => Some(Both(a, b)),
84+
}
85+
}
86+
}
87+
88+
#[unstable = "trait is unstable"]
89+
impl<A, B, T, U> ExactSizeIterator<EitherOrBoth<A, B>> for ZipLongest<T, U>
90+
where T: ExactSizeIterator<A>, U: ExactSizeIterator<B> {}
91+
92+
93+
/// A value yielded by `ZipLongest`.
94+
/// Contains one or two values,
95+
/// depending on which of the input iterators are exhausted.
96+
#[deriving(Clone, PartialEq, Eq, Show)]
97+
pub enum EitherOrBoth<A, B> {
98+
/// Neither input iterator is exhausted yet, yielding two values.
99+
Both(A, B),
100+
/// The parameter iterator of `.zip_longest()` is exhausted,
101+
/// only yielding a value from the `self` iterator.
102+
Left(A),
103+
/// The `self` iterator of `.zip_longest()` is exhausted,
104+
/// only yielding a value from the parameter iterator.
105+
Right(B),
106+
}

0 commit comments

Comments
 (0)