Skip to content

Commit 500e1e2

Browse files
committed
Add Iterator::zip_longest.
Like `Iterator::zip`, but the returned iterator is as long as the longest of the input iterators rather than shortest, and yields `(Option<A>, Option<B>)` (which can not both be `None`) rather than `(A, B)`. An alternative could be to yield a new `EitherOrBoth` (?) enum that is either `Both(A, B)`, `Left(A)`, or `Right(B)`. Precedent: https://docs.python.org/library/itertools.html#itertools.izip_longest
1 parent d9c7c00 commit 500e1e2

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

src/libcore/iter.rs

+90-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ pub trait IteratorExt<A>: Iterator<A> {
137137
///
138138
/// ```rust
139139
/// let a = [0i];
140-
/// let b = [1i];
140+
/// let b = [1i, 2i];
141141
/// let mut it = a.iter().zip(b.iter());
142142
/// let (x0, x1) = (0i, 1i);
143143
/// assert_eq!(it.next().unwrap(), (&x0, &x1));
@@ -149,6 +149,27 @@ pub trait IteratorExt<A>: Iterator<A> {
149149
Zip{a: self, b: other}
150150
}
151151

152+
/// Creates an iterator which iterates over both this and the specified
153+
/// iterators simultaneously, yielding pairs of two optional elements.
154+
/// When both iterators return None, all further invocations of next() will
155+
/// return None.
156+
///
157+
/// # Example
158+
///
159+
/// ```rust
160+
/// let a = [0i];
161+
/// let b = [1i, 2i];
162+
/// let mut it = a.iter().zip(b.iter());
163+
/// let (x0, x1, x2) = (0i, 1i, 2i);
164+
/// assert_eq!(it.next().unwrap(), (Some(&x0), Some(&x1)));
165+
/// assert_eq!(it.next().unwrap(), (None, Some(&x2)));
166+
/// assert!(it.next().is_none());
167+
/// ```
168+
#[inline]
169+
fn zip_longest<B, U: Iterator<B>>(self, other: U) -> ZipLongest<Self, U> {
170+
ZipLongest{a: self, b: other}
171+
}
172+
152173
/// Creates a new iterator which will apply the specified function to each
153174
/// element returned by the first, yielding the mapped element instead.
154175
///
@@ -780,6 +801,9 @@ impl<'a, A, B, T: ExactSizeIterator<A>> ExactSizeIterator<B> for Map<'a, A, B, T
780801
#[unstable = "trait is unstable"]
781802
impl<A, B, T, U> ExactSizeIterator<(A, B)> for Zip<T, U>
782803
where T: ExactSizeIterator<A>, U: ExactSizeIterator<B> {}
804+
#[unstable = "trait is unstable"]
805+
impl<A, B, T, U> ExactSizeIterator<(Option<A>, Option<B>)> for ZipLongest<T, U>
806+
where T: ExactSizeIterator<A>, U: ExactSizeIterator<B> {}
783807

784808
/// An double-ended iterator with the direction inverted
785809
#[deriving(Clone)]
@@ -1368,6 +1392,71 @@ RandomAccessIterator<(A, B)> for Zip<T, U> {
13681392
}
13691393
}
13701394

1395+
/// An iterator which iterates two other iterators simultaneously
1396+
#[deriving(Clone)]
1397+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
1398+
pub struct ZipLongest<T, U> {
1399+
a: T,
1400+
b: U
1401+
}
1402+
1403+
impl<A, B, T: Iterator<A>, U: Iterator<B>> Iterator<(Option<A>, Option<B>)> for ZipLongest<T, U> {
1404+
#[inline]
1405+
fn next(&mut self) -> Option<(Option<A>, Option<B>)> {
1406+
match (self.a.next(), self.b.next()) {
1407+
(None, None) => None,
1408+
pair_of_options => Some(pair_of_options),
1409+
}
1410+
}
1411+
1412+
#[inline]
1413+
fn size_hint(&self) -> (uint, Option<uint>) {
1414+
let (a_lower, a_upper) = self.a.size_hint();
1415+
let (b_lower, b_upper) = self.b.size_hint();
1416+
1417+
let lower = cmp::max(a_lower, b_lower);
1418+
1419+
let upper = match (a_upper, b_upper) {
1420+
(Some(x), Some(y)) => Some(cmp::max(x,y)),
1421+
_ => None
1422+
};
1423+
1424+
(lower, upper)
1425+
}
1426+
}
1427+
1428+
impl<A, B, T: ExactSize<A>, U: ExactSize<B>> DoubleEndedIterator<(Option<A>, Option<B>)>
1429+
for ZipLongest<T, U> {
1430+
#[inline]
1431+
fn next_back(&mut self) -> Option<(Option<A>, Option<B>)> {
1432+
use cmp::{Equal, Greater, Less};
1433+
match self.a.len().cmp(&self.b.len()) {
1434+
Equal => match (self.a.next_back(), self.b.next_back()) {
1435+
(None, None) => None,
1436+
pair_of_options => Some(pair_of_options),
1437+
},
1438+
Greater => self.a.next_back().map(|x| (Some(x), None)),
1439+
Less => self.b.next_back().map(|y| (None, Some(y))),
1440+
}
1441+
}
1442+
}
1443+
1444+
impl<A, B, T: RandomAccessIterator<A>, U: RandomAccessIterator<B>>
1445+
RandomAccessIterator<(Option<A>, Option<B>)> for ZipLongest<T, U> {
1446+
#[inline]
1447+
fn indexable(&self) -> uint {
1448+
cmp::max(self.a.indexable(), self.b.indexable())
1449+
}
1450+
1451+
#[inline]
1452+
fn idx(&mut self, index: uint) -> Option<(Option<A>, Option<B>)> {
1453+
match (self.a.idx(index), self.b.idx(index)) {
1454+
(None, None) => None,
1455+
pair_of_options => Some(pair_of_options),
1456+
}
1457+
}
1458+
}
1459+
13711460
/// An iterator which maps the values of `iter` with `f`
13721461
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
13731462
#[stable]

src/libcoretest/iter.rs

+25
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ fn test_iterator_size_hint() {
340340
assert_eq!(c.enumerate().size_hint(), (uint::MAX, None));
341341
assert_eq!(c.chain(vi.map(|&i| i)).size_hint(), (uint::MAX, None));
342342
assert_eq!(c.zip(vi).size_hint(), (10, Some(10)));
343+
assert_eq!(c.zip_longest(vi).size_hint(), (uint::MAX, None));
343344
assert_eq!(c.scan(0i, |_,_| Some(0i)).size_hint(), (0, None));
344345
assert_eq!(c.filter(|_| false).size_hint(), (0, None));
345346
assert_eq!(c.map(|_| 0i).size_hint(), (uint::MAX, None));
@@ -354,6 +355,7 @@ fn test_iterator_size_hint() {
354355
assert_eq!(vi.enumerate().size_hint(), (10, Some(10)));
355356
assert_eq!(vi.chain(v2.iter()).size_hint(), (13, Some(13)));
356357
assert_eq!(vi.zip(v2.iter()).size_hint(), (3, Some(3)));
358+
assert_eq!(vi.zip_longest(v2.iter()).size_hint(), (10, Some(10)));
357359
assert_eq!(vi.scan(0i, |_,_| Some(0i)).size_hint(), (0, Some(10)));
358360
assert_eq!(vi.filter(|_| false).size_hint(), (0, Some(10)));
359361
assert_eq!(vi.map(|&i| i+1).size_hint(), (10, Some(10)));
@@ -497,6 +499,22 @@ fn test_double_ended_zip() {
497499
assert_eq!(it.next(), None);
498500
}
499501

502+
#[test]
503+
fn test_double_ended_zip_longest() {
504+
let xs = [1i, 2, 3, 4, 5, 6];
505+
let ys = [1i, 2, 3, 7];
506+
let a = xs.iter().map(|&x| x);
507+
let b = ys.iter().map(|&x| x);
508+
let mut it = a.zip_longest(b);
509+
assert_eq!(it.next(), Some((Some(1), Some(1))));
510+
assert_eq!(it.next(), Some((Some(2), Some(2))));
511+
assert_eq!(it.next_back(), Some((Some(6), None)));
512+
assert_eq!(it.next_back(), Some((Some(5), None)));
513+
assert_eq!(it.next_back(), Some((Some(4), Some(7))));
514+
assert_eq!(it.next(), Some((Some(3), Some(3))));
515+
assert_eq!(it.next(), None);
516+
}
517+
500518
#[test]
501519
fn test_double_ended_filter() {
502520
let xs = [1i, 2, 3, 4, 5, 6];
@@ -641,6 +659,13 @@ fn test_random_access_zip() {
641659
check_randacc_iter(xs.iter().zip(ys.iter()), cmp::min(xs.len(), ys.len()));
642660
}
643661

662+
#[test]
663+
fn test_random_access_zip_longest() {
664+
let xs = [1i, 2, 3, 4, 5];
665+
let ys = [7i, 9, 11];
666+
check_randacc_iter(xs.iter().zip_longest(ys.iter()), cmp::max(xs.len(), ys.len()));
667+
}
668+
644669
#[test]
645670
fn test_random_access_take() {
646671
let xs = [1i, 2, 3, 4, 5];

0 commit comments

Comments
 (0)