Skip to content

Commit

Permalink
Merge #300
Browse files Browse the repository at this point in the history
300: Add Itertools::{sum1, product1} r=jswrenn a=Emerentius

This implements `sum1` and `product1` consumers that are like `sum` and `product` except they produce an `Option<T>` with `Some` for nonempty iters and `None` otherwise, just like `fold1`.
That allows one to distinguish between an empty iter and iters that sum/multiply to their neutral element.

Co-authored-by: Emerentius <emerentius@arcor.de>
  • Loading branch information
bors[bot] and Emerentius committed Oct 1, 2019
2 parents 9b7eefb + 142659f commit 340e06f
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 1 deletion.
60 changes: 59 additions & 1 deletion src/lib.rs
Expand Up @@ -56,7 +56,7 @@ pub use either::Either;

#[cfg(feature = "use_std")]
use std::collections::HashMap;
use std::iter::{IntoIterator};
use std::iter::{IntoIterator, once};
use std::cmp::Ordering;
use std::fmt;
#[cfg(feature = "use_std")]
Expand Down Expand Up @@ -1928,6 +1928,64 @@ pub trait Itertools : Iterator {
FoldWhile::Continue(acc)
}

/// Iterate over the entire iterator and add all the elements.
///
/// An empty iterator returns `None`, otherwise `Some(sum)`.
///
/// # Panics
///
/// When calling `sum1()` and a primitive integer type is being returned, this
/// method will panic if the computation overflows and debug assertions are
/// enabled.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let empty_sum = (1..1).sum1::<i32>();
/// assert_eq!(empty_sum, None);
///
/// let nonempty_sum = (1..11).sum1::<i32>();
/// assert_eq!(nonempty_sum, Some(55));
/// ```
fn sum1<S>(mut self) -> Option<S>
where Self: Sized,
S: std::iter::Sum<Self::Item>,
{
self.next()
.map(|first| once(first).chain(self).sum())
}

/// Iterate over the entire iterator and multiply all the elements.
///
/// An empty iterator returns `None`, otherwise `Some(product)`.
///
/// # Panics
///
/// When calling `product1()` and a primitive integer type is being returned,
/// method will panic if the computation overflows and debug assertions are
/// enabled.
///
/// # Examples
/// ```
/// use itertools::Itertools;
///
/// let empty_product = (1..1).product1::<i32>();
/// assert_eq!(empty_product, None);
///
/// let nonempty_product = (1..11).product1::<i32>();
/// assert_eq!(nonempty_product, Some(3628800));
/// ```
fn product1<P>(mut self) -> Option<P>
where Self: Sized,
P: std::iter::Product<Self::Item>,
{
self.next()
.map(|first| once(first).chain(self).product())
}


/// Sort all iterator elements into a new iterator in ascending order.
///
/// **Note:** This consumes the entire iterator, uses the
Expand Down
18 changes: 18 additions & 0 deletions tests/test_core.rs
Expand Up @@ -252,3 +252,21 @@ fn exactly_one() {
assert!((0..10).filter(|&x| x > 1 && x < 5).exactly_one().unwrap_err().eq(2..5));
assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0));
}

#[test]
fn sum1() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(v[..0].iter().cloned().sum1::<i32>(), None);
assert_eq!(v[1..2].iter().cloned().sum1::<i32>(), Some(1));
assert_eq!(v[1..3].iter().cloned().sum1::<i32>(), Some(3));
assert_eq!(v.iter().cloned().sum1::<i32>(), Some(55));
}

#[test]
fn product1() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(v[..0].iter().cloned().product1::<i32>(), None);
assert_eq!(v[..1].iter().cloned().product1::<i32>(), Some(0));
assert_eq!(v[1..3].iter().cloned().product1::<i32>(), Some(2));
assert_eq!(v[1..5].iter().cloned().product1::<i32>(), Some(24));
}

0 comments on commit 340e06f

Please sign in to comment.