From 7a7d8c188eeffee70786b269e29607eb9b151614 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 d7c7960bb..0a7d7c913 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,6 +168,8 @@ mod combinations; mod combinations_with_replacement; mod exactly_one_err; mod diff; +#[cfg(feature = "use_std")] +mod extrema_set; mod format; #[cfg(feature = "use_std")] mod group_map; @@ -2142,6 +2144,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 4f48c55cf..51c81c72f 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -670,6 +670,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;