Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minmax set (prev "Add {min,max}_set(_by{_key)?)? functions") #613

Merged
merged 8 commits into from May 23, 2022
48 changes: 48 additions & 0 deletions src/extrema_set.rs
@@ -0,0 +1,48 @@
use std::cmp::Ordering;

/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
pub fn min_set_impl<I, K, F, Compare>(
mut it: I,
mut key_for: F,
mut compare: Compare,
) -> Vec<I::Item>
where
I: Iterator,
F: FnMut(&I::Item) -> K,
Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
{
match it.next() {
None => Vec::new(),
Some(element) => {
let mut current_key = key_for(&element);
let mut result = vec![element];
it.for_each(|element| {
let key = key_for(&element);
match compare(&element, &result[0], &key, &current_key) {
Ordering::Less => {
result.clear();
result.push(element);
current_key = key;
}
Ordering::Equal => {
result.push(element);
}
Ordering::Greater => {}
}
});
result
}
}
}

/// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`.
pub fn max_set_impl<I, K, F, Compare>(it: I, key_for: F, mut compare: Compare) -> Vec<I::Item>
where
I: Iterator,
F: FnMut(&I::Item) -> K,
Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
{
min_set_impl(it, key_for, |it1, it2, key1, key2| {
compare(it2, it1, key2, key1)
})
}
190 changes: 190 additions & 0 deletions src/lib.rs
Expand Up @@ -197,6 +197,8 @@ mod combinations_with_replacement;
mod exactly_one_err;
mod diff;
mod flatten_ok;
#[cfg(feature = "use_std")]
mod extrema_set;
mod format;
#[cfg(feature = "use_std")]
mod grouping_map;
Expand Down Expand Up @@ -2902,6 +2904,194 @@ pub trait Itertools : Iterator {
grouping_map::new(grouping_map::MapForGrouping::new(self, key_mapper))
}

/// Return all minimum elements of an iterator.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let a: [i32; 0] = [];
/// assert_eq!(a.iter().min_set(), Vec::<&i32>::new());
///
/// let a = [1];
/// assert_eq!(a.iter().min_set(), vec![&1]);
///
/// let a = [1, 2, 3, 4, 5];
/// assert_eq!(a.iter().min_set(), vec![&1]);
///
/// let a = [1, 1, 1, 1];
/// assert_eq!(a.iter().min_set(), 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) -> Vec<Self::Item>
where Self: Sized, Self::Item: Ord
{
extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(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), Vec::<&(i32, i32)>::new());
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), 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)), 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)), 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) -> Vec<Self::Item>
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
{
extrema_set::min_set_impl(
self,
|_| (),
|x, y, _, _| 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(|_| ()), Vec::<&(i32, i32)>::new());
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().min_set_by_key(|&&(k,_)| k), 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), 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), 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) -> Vec<Self::Item>
where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
{
extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
}

/// Return all maximum elements of an iterator.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let a: [i32; 0] = [];
/// assert_eq!(a.iter().max_set(), Vec::<&i32>::new());
///
/// let a = [1];
/// assert_eq!(a.iter().max_set(), vec![&1]);
///
/// let a = [1, 2, 3, 4, 5];
/// assert_eq!(a.iter().max_set(), vec![&5]);
///
/// let a = [1, 1, 1, 1];
/// assert_eq!(a.iter().max_set(), 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) -> Vec<Self::Item>
where Self: Sized, Self::Item: Ord
{
extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(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), Vec::<&(i32, i32)>::new());
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), 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)), 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)), 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) -> Vec<Self::Item>
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
{
extrema_set::max_set_impl(
self,
|_| (),
|x, y, _, _| 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(|_| ()), Vec::<&(i32, i32)>::new());
///
/// let a = [(1, 2)];
/// assert_eq!(a.iter().max_set_by_key(|&&(k,_)| k), 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), 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), 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) -> Vec<Self::Item>
where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
{
extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
}

/// Return the minimum and maximum elements in the iterator.
///
/// The return type `MinMaxResult` is an enum of three variants:
Expand Down
59 changes: 59 additions & 0 deletions tests/quick.rs
Expand Up @@ -1693,3 +1693,62 @@ quickcheck! {
}
}

quickcheck! {
fn min_set_contains_min(a: Vec<(usize, char)>) -> bool {
let result_set = a.iter().min_set();
if let Some(result_element) = a.iter().min() {
result_set.contains(&result_element)
} else {
result_set.is_empty()
}
}

fn min_set_by_contains_min(a: Vec<(usize, char)>) -> bool {
let compare = |x: &&(usize, char), y: &&(usize, char)| x.1.cmp(&y.1);
let result_set = a.iter().min_set_by(compare);
if let Some(result_element) = a.iter().min_by(compare) {
result_set.contains(&result_element)
} else {
result_set.is_empty()
}
}

fn min_set_by_key_contains_min(a: Vec<(usize, char)>) -> bool {
let key = |x: &&(usize, char)| x.1;
let result_set = a.iter().min_set_by_key(&key);
if let Some(result_element) = a.iter().min_by_key(&key) {
result_set.contains(&result_element)
} else {
result_set.is_empty()
}
}

fn max_set_contains_max(a: Vec<(usize, char)>) -> bool {
let result_set = a.iter().max_set();
if let Some(result_element) = a.iter().max() {
result_set.contains(&result_element)
} else {
result_set.is_empty()
}
}

fn max_set_by_contains_max(a: Vec<(usize, char)>) -> bool {
let compare = |x: &&(usize, char), y: &&(usize, char)| x.1.cmp(&y.1);
let result_set = a.iter().max_set_by(compare);
if let Some(result_element) = a.iter().max_by(compare) {
result_set.contains(&result_element)
} else {
result_set.is_empty()
}
}

fn max_set_by_key_contains_max(a: Vec<(usize, char)>) -> bool {
let key = |x: &&(usize, char)| x.1;
let result_set = a.iter().max_set_by_key(&key);
if let Some(result_element) = a.iter().max_by_key(&key) {
result_set.contains(&result_element)
} else {
result_set.is_empty()
}
}
}
50 changes: 49 additions & 1 deletion tests/test_std.rs
Expand Up @@ -992,6 +992,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::<u32>.iter().min_set(), Vec::<&u32>::new());
assert_eq!(None::<u32>.iter().max_set(), Vec::<&u32>::new());

assert_eq!(Some(1u32).iter().min_set(), vec![&1]);
assert_eq!(Some(1u32).iter().max_set(), 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();
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);
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));
assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]);

let max_set = data.iter().max_set();
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);
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));
assert_eq!(max_set_by, vec![&Val(0, 2)]);
}

#[test]
fn minmax() {
use std::cmp::Ordering;
Expand Down Expand Up @@ -1119,4 +1167,4 @@ fn multiunzip() {
let (): () = [(), (), ()].iter().cloned().multiunzip();
let t: (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)].iter().cloned().multiunzip();
assert_eq!(t, (vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11]));
}
}