From 0eb8bf3d4fbef2e4fda4e3136b8a023690423e92 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Tue, 11 Dec 2018 16:28:55 +0100 Subject: [PATCH] FEAT: Add {min,max}_set(_by{_key)?)? functions 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. --- src/extrema_set.rs | 42 ++++++++++ src/lib.rs | 190 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_std.rs | 48 ++++++++++++ 3 files changed, 280 insertions(+) create mode 100644 src/extrema_set.rs diff --git a/src/extrema_set.rs b/src/extrema_set.rs new file mode 100644 index 000000000..e6d5eec0b --- /dev/null +++ b/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(mut it: I, + mut key_for: F, + mut lt: L) -> Option> + 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, ¤t_key) { + result.clear(); + result.push(element); + current_key = key; + } else if !lt(&result[0], &element, ¤t_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(it: I, + key_for: F, + mut lt: L) -> Option> + 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)) +} + + diff --git a/src/lib.rs b/src/lib.rs index 91ff7d3e6..4d58c68c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -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> + 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(self, mut compare: F) -> Option> + 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(self, key: F) -> Option> + 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> + 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(self, mut compare: F) -> Option> + 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(self, key: F) -> Option> + 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: diff --git a/tests/test_std.rs b/tests/test_std.rs index b587f4470..0eddf8ffb 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -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 for Val { + fn partial_cmp(&self, other: &Val) -> Option { + self.0.partial_cmp(&other.0) + } + } + + impl Ord for Val { + fn cmp(&self, other: &Val) -> Ordering { + self.0.cmp(&other.0) + } + } + + assert_eq!(None::>.iter().min_set(), None); + assert_eq!(None::>.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;