From e8c67ef406a95e27ec030facad93da9d97c656e0 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Tue, 11 Dec 2018 16:28:55 +0100 Subject: [PATCH 1/8] 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 f2ecd3ec7..29408ba02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -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(), 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 2049d1597..cf41b8843 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -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 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; From bb8b41a88105e9f74af45eaa1d92d1f60cc2c247 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 2/8] Add {min,max}_set(_by{_key)?)? functions (2) return Vec instead Option --- src/extrema_set.rs | 8 +++---- src/lib.rs | 60 +++++++++++++++++++++++----------------------- tests/test_std.rs | 22 ++++++++--------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/extrema_set.rs b/src/extrema_set.rs index e6d5eec0b..60f8979a9 100644 --- a/src/extrema_set.rs +++ b/src/extrema_set.rs @@ -1,13 +1,13 @@ /// 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> + mut lt: L) -> Vec 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, + None => return Vec::new(), Some(element) => { let key = key_for(&element); (vec![element], key) @@ -25,13 +25,13 @@ pub fn min_set_impl(mut it: I, } } - Some(result) + 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> + mut lt: L) -> Vec where I: Iterator, F: FnMut(&I::Item) -> K, L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, diff --git a/src/lib.rs b/src/lib.rs index 29408ba02..791de1c71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2912,22 +2912,22 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let a: [i32; 0] = []; - /// assert_eq!(a.iter().min_set(), None); + /// assert_eq!(a.iter().min_set(), Vec::<&i32>::new()); /// /// let a = [1]; - /// assert_eq!(a.iter().min_set(), Some(vec![&1])); + /// assert_eq!(a.iter().min_set(), vec![&1]); /// /// let a = [1, 2, 3, 4, 5]; - /// assert_eq!(a.iter().min_set(), Some(vec![&1])); + /// assert_eq!(a.iter().min_set(), vec![&1]); /// /// let a = [1, 1, 1, 1]; - /// assert_eq!(a.iter().min_set(), Some(vec![&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) -> Option> + fn min_set(self) -> Vec where Self: Sized, Self::Item: PartialOrd { extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x < y) @@ -2943,22 +2943,22 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; - /// assert_eq!(a.iter().min_set_by(|_, _| Ordering::Equal), None); + /// 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)), Some(vec![&(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)), Some(vec![&(1, 2), &(2, 2)])); + /// 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)), Some(vec![&(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(self, mut compare: F) -> Option> + fn min_set_by(self, mut compare: F) -> Vec where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering { extrema_set::min_set_impl( @@ -2977,22 +2977,22 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; - /// assert_eq!(a.iter().min_set_by_key(|_| ()), None); + /// 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), Some(vec![&(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), Some(vec![&(1, 2), &(2, 2)])); + /// 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), Some(vec![&(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(self, key: F) -> Option> + fn min_set_by_key(self, key: F) -> Vec where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K { extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx < ky) @@ -3006,22 +3006,22 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let a: [i32; 0] = []; - /// assert_eq!(a.iter().max_set(), None); + /// assert_eq!(a.iter().max_set(), Vec::<&i32>::new()); /// /// let a = [1]; - /// assert_eq!(a.iter().max_set(), Some(vec![&1])); + /// assert_eq!(a.iter().max_set(), vec![&1]); /// /// let a = [1, 2, 3, 4, 5]; - /// assert_eq!(a.iter().max_set(), Some(vec![&5])); + /// assert_eq!(a.iter().max_set(), vec![&5]); /// /// let a = [1, 1, 1, 1]; - /// assert_eq!(a.iter().max_set(), Some(vec![&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) -> Option> + fn max_set(self) -> Vec where Self: Sized, Self::Item: PartialOrd { extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x < y) @@ -3037,22 +3037,22 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; - /// assert_eq!(a.iter().max_set_by(|_, _| Ordering::Equal), None); + /// 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)), Some(vec![&(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)), Some(vec![&(3, 9), &(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)), Some(vec![&(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(self, mut compare: F) -> Option> + fn max_set_by(self, mut compare: F) -> Vec where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering { extrema_set::max_set_impl( @@ -3071,22 +3071,22 @@ pub trait Itertools : Iterator { /// use itertools::Itertools; /// /// let a: [(i32, i32); 0] = []; - /// assert_eq!(a.iter().max_set_by_key(|_| ()), None); + /// 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), Some(vec![&(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), Some(vec![&(3, 9), &(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), Some(vec![&(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(self, key: F) -> Option> + fn max_set_by_key(self, key: F) -> Vec where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K { extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx < ky) diff --git a/tests/test_std.rs b/tests/test_std.rs index cf41b8843..4463d912f 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -1013,30 +1013,30 @@ fn extrema_set() { } } - assert_eq!(None::>.iter().min_set(), None); - assert_eq!(None::>.iter().max_set(), None); + assert_eq!(None::.iter().min_set(), Vec::<&u32>::new()); + assert_eq!(None::.iter().max_set(), Vec::<&u32>::new()); - assert_eq!(Some(1u32).iter().min_set(), Some(vec![&1])); - assert_eq!(Some(1u32).iter().max_set(), Some(vec![&1])); + 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().unwrap(); + 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).unwrap(); + 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)).unwrap(); + 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().unwrap(); + 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).unwrap(); + 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)).unwrap(); + 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)]); } @@ -1167,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])); -} \ No newline at end of file +} From 491b34a37faa11ea6ce103e43dcd5fea7758810d Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 3/8] Add {min,max}_set(_by{_key)?)? functions (3) use Ord instead of PartialOrd We may relax this bound at some point, but I'd go with this until we have evidence that it bothers users, as there have been cases where I actually was happy to be informed about Ord vs PartialOrd. --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 791de1c71..3f058b1f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2928,7 +2928,7 @@ pub trait Itertools : Iterator { /// if an element is NaN. #[cfg(feature = "use_std")] fn min_set(self) -> Vec - where Self: Sized, Self::Item: PartialOrd + where Self: Sized, Self::Item: Ord { extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x < y) } @@ -2993,7 +2993,7 @@ pub trait Itertools : Iterator { /// if an element is NaN. #[cfg(feature = "use_std")] fn min_set_by_key(self, key: F) -> Vec - where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K + where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K { extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx < ky) } @@ -3022,7 +3022,7 @@ pub trait Itertools : Iterator { /// if an element is NaN. #[cfg(feature = "use_std")] fn max_set(self) -> Vec - where Self: Sized, Self::Item: PartialOrd + where Self: Sized, Self::Item: Ord { extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x < y) } @@ -3087,7 +3087,7 @@ pub trait Itertools : Iterator { /// if an element is NaN. #[cfg(feature = "use_std")] fn max_set_by_key(self, key: F) -> Vec - where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K + where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K { extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx < ky) } From 834bcc5b9b293ef438ee57451b9fb555fc5b4e15 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 4/8] Add {min,max}_set(_by{_key)?)? functions (4) use internal iteration We may relax this bound at some point, but I'd go with this until we have evidence that it bothers users, as there have been cases where I actually was happy to be informed about Ord vs PartialOrd. --- src/extrema_set.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extrema_set.rs b/src/extrema_set.rs index 60f8979a9..3c0794808 100644 --- a/src/extrema_set.rs +++ b/src/extrema_set.rs @@ -14,7 +14,7 @@ pub fn min_set_impl(mut it: I, } }; - for element in it { + it.for_each(|element| { let key = key_for(&element); if lt(&element, &result[0], &key, ¤t_key) { result.clear(); @@ -23,7 +23,7 @@ pub fn min_set_impl(mut it: I, } else if !lt(&result[0], &element, ¤t_key, &key) { result.push(element); } - } + }); result } From 6f96e0142e93ee07b986317eadae54e8f7e554bf Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 5/8] Add {min,max}_set(_by{_key)?)? functions (5) comparator uses Ordering instead of bool More canonical. --- src/extrema_set.rs | 33 ++++++++++++++++++++------------- src/lib.rs | 12 ++++++------ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/extrema_set.rs b/src/extrema_set.rs index 3c0794808..2c7cef681 100644 --- a/src/extrema_set.rs +++ b/src/extrema_set.rs @@ -1,10 +1,12 @@ +use std::cmp::Ordering; + /// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`. -pub fn min_set_impl(mut it: I, +pub fn min_set_impl(mut it: I, mut key_for: F, - mut lt: L) -> Vec + mut compare: Compare) -> Vec where I: Iterator, F: FnMut(&I::Item) -> K, - L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, + Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { let (mut result, mut current_key) = match it.next() { None => return Vec::new(), @@ -16,12 +18,17 @@ pub fn min_set_impl(mut it: I, it.for_each(|element| { 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); + match compare(&element, &result[0], &key, ¤t_key) { + Ordering::Less => { + result.clear(); + result.push(element); + current_key = key; + }, + Ordering::Equal => { + result.push(element); + }, + Ordering::Greater => { + }, } }); @@ -29,14 +36,14 @@ pub fn min_set_impl(mut it: I, } /// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`. -pub fn max_set_impl(it: I, +pub fn max_set_impl(it: I, key_for: F, - mut lt: L) -> Vec + mut compare: Compare) -> Vec where I: Iterator, F: FnMut(&I::Item) -> K, - L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, + Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { - min_set_impl(it, key_for, |it1, it2, key1, key2| lt(it2, it1, key2, key1)) + min_set_impl(it, key_for, |it1, it2, key1, key2| compare(it2, it1, key2, key1)) } diff --git a/src/lib.rs b/src/lib.rs index 3f058b1f8..c2c1750be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2930,7 +2930,7 @@ pub trait Itertools : Iterator { fn min_set(self) -> Vec where Self: Sized, Self::Item: Ord { - extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x < y) + extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(y)) } /// Return all minimum elements of an iterator, as determined by @@ -2964,7 +2964,7 @@ pub trait Itertools : Iterator { extrema_set::min_set_impl( self, |_| (), - |x, y, _, _| Ordering::Less == compare(x, y) + |x, y, _, _| compare(x, y) ) } @@ -2995,7 +2995,7 @@ pub trait Itertools : Iterator { fn min_set_by_key(self, key: F) -> Vec where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K { - extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx < ky) + extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky)) } /// Return all maximum elements of an iterator. @@ -3024,7 +3024,7 @@ pub trait Itertools : Iterator { fn max_set(self) -> Vec where Self: Sized, Self::Item: Ord { - extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x < y) + extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(y)) } /// Return all maximum elements of an iterator, as determined by @@ -3058,7 +3058,7 @@ pub trait Itertools : Iterator { extrema_set::max_set_impl( self, |_| (), - |x, y, _, _| Ordering::Less == compare(x, y) + |x, y, _, _| compare(x, y) ) } @@ -3089,7 +3089,7 @@ pub trait Itertools : Iterator { fn max_set_by_key(self, key: F) -> Vec where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K { - extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx < ky) + extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky)) } /// Return the minimum and maximum elements in the iterator. From fb57fc62a88ab745ab2f3206b1f5c8a4004614ab Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 6/8] Add {min,max}_set(_by{_key)?)? functions (6) inline bulk into match arm Slightly simplifies control flow as it avoids mutable variables that may not even be used in case of early return. --- src/extrema_set.rs | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/extrema_set.rs b/src/extrema_set.rs index 2c7cef681..88db0ed94 100644 --- a/src/extrema_set.rs +++ b/src/extrema_set.rs @@ -8,31 +8,30 @@ pub fn min_set_impl(mut it: I, F: FnMut(&I::Item) -> K, Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { - let (mut result, mut current_key) = match it.next() { - None => return Vec::new(), + match it.next() { + None => Vec::new(), Some(element) => { - let key = key_for(&element); - (vec![element], key) + 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, ¤t_key) { + Ordering::Less => { + result.clear(); + result.push(element); + current_key = key; + }, + Ordering::Equal => { + result.push(element); + }, + Ordering::Greater => { + }, + } + }); + result } - }; + } - it.for_each(|element| { - let key = key_for(&element); - match compare(&element, &result[0], &key, ¤t_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`. From 08a10da17a2c7f2cb4b38bac02077a26a97ecc67 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 7/8] Add {min,max}_set(_by{_key)?)? functions (7) cargo fmt extrema_set.rs --- src/extrema_set.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/extrema_set.rs b/src/extrema_set.rs index 88db0ed94..ae128364c 100644 --- a/src/extrema_set.rs +++ b/src/extrema_set.rs @@ -1,12 +1,15 @@ use std::cmp::Ordering; /// 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 compare: Compare) -> Vec - where I: Iterator, - F: FnMut(&I::Item) -> K, - Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, +pub fn min_set_impl( + mut it: I, + mut key_for: F, + mut compare: Compare, +) -> Vec +where + I: Iterator, + F: FnMut(&I::Item) -> K, + Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, { match it.next() { None => Vec::new(), @@ -20,29 +23,26 @@ pub fn min_set_impl(mut it: I, result.clear(); result.push(element); current_key = key; - }, + } Ordering::Equal => { result.push(element); - }, - Ordering::Greater => { - }, + } + Ordering::Greater => {} } }); 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 compare: Compare) -> Vec - where I: Iterator, - F: FnMut(&I::Item) -> K, - Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering, +pub fn max_set_impl(it: I, key_for: F, mut compare: Compare) -> Vec +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)) + min_set_impl(it, key_for, |it1, it2, key1, key2| { + compare(it2, it1, key2, key1) + }) } - - From 846219fe89d07f62224f0c16f8f0e65bde98b4c4 Mon Sep 17 00:00:00 2001 From: philipp Date: Wed, 4 May 2022 22:09:14 +0200 Subject: [PATCH 8/8] Add {min,max}_set(_by{_key)?)? functions (8) add quickcheck tests the {min,max}_set results must contain the result of the corresponding {min,max} variant. --- tests/quick.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/quick.rs b/tests/quick.rs index 7e222a641..5829d1c1d 100644 --- a/tests/quick.rs +++ b/tests/quick.rs @@ -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() + } + } +}