Skip to content

Commit

Permalink
FEAT: Add {min,max}_set(_by{_key)?)? functions
Browse files Browse the repository at this point in the history
The function min_set returns a Vec of all the minimum values. All
variants for max and with key extraction and comparison functions are
added also.

Since the functions need to return an unknown number of values and the
values are not known until the iterator is finished, the function
needs to allocate memory. Therefore Vec is used for returning the
values.
  • Loading branch information
zayenz committed Dec 11, 2018
1 parent 3bf265d commit 0eb8bf3
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/extrema_set.rs
@@ -0,0 +1,42 @@
/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
pub fn min_set_impl<I, K, F, L>(mut it: I,
mut key_for: F,
mut lt: L) -> Option<Vec<I::Item>>
where I: Iterator,
F: FnMut(&I::Item) -> K,
L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
{
let (mut result, mut current_key) = match it.next() {
None => return None,
Some(element) => {
let key = key_for(&element);
(vec![element], key)
}
};

for element in it {
let key = key_for(&element);
if lt(&element, &result[0], &key, &current_key) {
result.clear();
result.push(element);
current_key = key;
} else if !lt(&result[0], &element, &current_key, &key) {
result.push(element);
}
}

Some(result)
}

/// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`.
pub fn max_set_impl<I, K, F, L>(it: I,
key_for: F,
mut lt: L) -> Option<Vec<I::Item>>
where I: Iterator,
F: FnMut(&I::Item) -> K,
L: FnMut(&I::Item, &I::Item, &K, &K) -> bool,
{
min_set_impl(it, key_for, |it1, it2, key1, key2| lt(it2, it1, key2, key1))
}


190 changes: 190 additions & 0 deletions src/lib.rs
Expand Up @@ -157,6 +157,8 @@ mod cons_tuples_impl;
#[cfg(feature = "use_std")]
mod combinations;
mod diff;
#[cfg(feature = "use_std")]
mod extrema_set;
mod format;
#[cfg(feature = "use_std")]
mod group_map;
Expand Down Expand Up @@ -1947,6 +1949,194 @@ pub trait Itertools : Iterator {
group_map::into_group_map(self)
}

/// Return all minimum elements of an iterator.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let a: [i32; 0] = [];
/// assert_eq!(a.iter().min_set(), None);
///
/// let a = [1];
/// assert_eq!(a.iter().min_set(), Some(vec![&1]));
///
/// let a = [1, 2, 3, 4, 5];
/// assert_eq!(a.iter().min_set(), Some(vec![&1]));
///
/// let a = [1, 1, 1, 1];
/// assert_eq!(a.iter().min_set(), Some(vec![&1, &1, &1, &1]));
/// ```
///
/// The elements can be floats but no particular result is guaranteed
/// if an element is NaN.
#[cfg(feature = "use_std")]
fn min_set(self) -> Option<Vec<Self::Item>>
where Self: Sized, Self::Item: PartialOrd
{
extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x < y)
}

/// Return all minimum elements of an iterator, as determined by
/// the specified function.
///
/// # Examples
///
/// ```
/// # use std::cmp::Ordering;
/// use itertools::Itertools;
///
/// let a: [(i32, i32); 0] = [];
/// assert_eq!(a.iter().min_set_by(|_, _| Ordering::Equal), None);
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), Some(vec![&(1, 2)]));
///
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
/// assert_eq!(a.iter().min_set_by(|&&(_,k1), &&(_,k2)| k1.cmp(&k2)), Some(vec![&(1, 2), &(2, 2)]));
///
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
/// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), Some(vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]));
/// ```
///
/// The elements can be floats but no particular result is guaranteed
/// if an element is NaN.
#[cfg(feature = "use_std")]
fn min_set_by<F>(self, mut compare: F) -> Option<Vec<Self::Item>>
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
{
extrema_set::min_set_impl(
self,
|_| (),
|x, y, _, _| Ordering::Less == compare(x, y)
)
}

/// Return all minimum elements of an iterator, as determined by
/// the specified function.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let a: [(i32, i32); 0] = [];
/// assert_eq!(a.iter().min_set_by_key(|_| ()), None);
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().min_set_by_key(|&&(k,_)| k), Some(vec![&(1, 2)]));
///
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
/// assert_eq!(a.iter().min_set_by_key(|&&(_, k)| k), Some(vec![&(1, 2), &(2, 2)]));
///
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
/// assert_eq!(a.iter().min_set_by_key(|&&(k, _)| k), Some(vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]));
/// ```
///
/// The elements can be floats but no particular result is guaranteed
/// if an element is NaN.
#[cfg(feature = "use_std")]
fn min_set_by_key<K, F>(self, key: F) -> Option<Vec<Self::Item>>
where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
{
extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx < ky)
}

/// Return all maximum elements of an iterator.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let a: [i32; 0] = [];
/// assert_eq!(a.iter().max_set(), None);
///
/// let a = [1];
/// assert_eq!(a.iter().max_set(), Some(vec![&1]));
///
/// let a = [1, 2, 3, 4, 5];
/// assert_eq!(a.iter().max_set(), Some(vec![&5]));
///
/// let a = [1, 1, 1, 1];
/// assert_eq!(a.iter().max_set(), Some(vec![&1, &1, &1, &1]));
/// ```
///
/// The elements can be floats but no particular result is guaranteed
/// if an element is NaN.
#[cfg(feature = "use_std")]
fn max_set(self) -> Option<Vec<Self::Item>>
where Self: Sized, Self::Item: PartialOrd
{
extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x < y)
}

/// Return all maximum elements of an iterator, as determined by
/// the specified function.
///
/// # Examples
///
/// ```
/// # use std::cmp::Ordering;
/// use itertools::Itertools;
///
/// let a: [(i32, i32); 0] = [];
/// assert_eq!(a.iter().max_set_by(|_, _| Ordering::Equal), None);
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), Some(vec![&(1, 2)]));
///
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
/// assert_eq!(a.iter().max_set_by(|&&(_,k1), &&(_,k2)| k1.cmp(&k2)), Some(vec![&(3, 9), &(5, 9)]));
///
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
/// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), Some(vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]));
/// ```
///
/// The elements can be floats but no particular result is guaranteed
/// if an element is NaN.
#[cfg(feature = "use_std")]
fn max_set_by<F>(self, mut compare: F) -> Option<Vec<Self::Item>>
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
{
extrema_set::max_set_impl(
self,
|_| (),
|x, y, _, _| Ordering::Less == compare(x, y)
)
}

/// Return all minimum elements of an iterator, as determined by
/// the specified function.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let a: [(i32, i32); 0] = [];
/// assert_eq!(a.iter().max_set_by_key(|_| ()), None);
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().max_set_by_key(|&&(k,_)| k), Some(vec![&(1, 2)]));
///
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
/// assert_eq!(a.iter().max_set_by_key(|&&(_, k)| k), Some(vec![&(3, 9), &(5, 9)]));
///
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
/// assert_eq!(a.iter().max_set_by_key(|&&(k, _)| k), Some(vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]));
/// ```
///
/// The elements can be floats but no particular result is guaranteed
/// if an element is NaN.
#[cfg(feature = "use_std")]
fn max_set_by_key<K, F>(self, key: F) -> Option<Vec<Self::Item>>
where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
{
extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx < ky)
}

/// Return the minimum and maximum elements in the iterator.
///
/// The return type `MinMaxResult` is an enum of three variants:
Expand Down
48 changes: 48 additions & 0 deletions tests/test_std.rs
Expand Up @@ -624,6 +624,54 @@ fn diff_shorter() {
});
}

#[test]
fn extrema_set() {
use std::cmp::Ordering;

// A peculiar type: Equality compares both tuple items, but ordering only the
// first item. Used to distinguish equal elements.
#[derive(Clone, Debug, PartialEq, Eq)]
struct Val(u32, u32);

impl PartialOrd<Val> for Val {
fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}

impl Ord for Val {
fn cmp(&self, other: &Val) -> Ordering {
self.0.cmp(&other.0)
}
}

assert_eq!(None::<Option<u32>>.iter().min_set(), None);
assert_eq!(None::<Option<u32>>.iter().max_set(), None);

assert_eq!(Some(1u32).iter().min_set(), Some(vec![&1]));
assert_eq!(Some(1u32).iter().max_set(), Some(vec![&1]));

let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];

let min_set = data.iter().min_set().unwrap();
assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]);

let min_set_by_key = data.iter().min_set_by_key(|v| v.1).unwrap();
assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]);

let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1)).unwrap();
assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]);

let max_set = data.iter().max_set().unwrap();
assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]);

let max_set_by_key = data.iter().max_set_by_key(|v| v.1).unwrap();
assert_eq!(max_set_by_key, vec![&Val(0, 2)]);

let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1)).unwrap();
assert_eq!(max_set_by, vec![&Val(0, 2)]);
}

#[test]
fn minmax() {
use std::cmp::Ordering;
Expand Down

0 comments on commit 0eb8bf3

Please sign in to comment.