try_partialord/
min_max.rs

1use crate::{InvalidOrderError, OrderResult};
2use core::cmp::Ordering;
3
4/// Min and max methods for [`PartialOrd`]
5/// ```
6/// use try_partialord::*;
7/// use rand::distributions::Standard;
8/// use rand::prelude::*;
9///
10/// let rng = thread_rng();
11/// let mut v: Vec<f32> = Standard.sample_iter(rng).take(100).collect();
12/// let min = v.iter().try_min().unwrap();
13/// assert_eq!(min, v.iter().min_by(|a, b| a.partial_cmp(b).unwrap()));
14///
15/// // min is error because of uncompareabale value `NAN`
16/// v.push(f32::NAN);
17/// let min = v.iter().try_min();
18/// assert!(min.is_err());
19/// ```
20pub trait TryMinMax<T> {
21    /// `PartialOrd` version for [`Iterator::min`].
22    #[inline]
23    fn try_min(self) -> OrderResult<Option<T>>
24    where
25        T: PartialOrd<T>,
26        Self: Sized,
27    {
28        self.try_select_by(|a, b| a.partial_cmp(b), Ordering::Greater)
29    }
30    /// `PartialOrd` version for [`Iterator::min_by`].
31    #[inline]
32    fn try_min_by<F>(self, compare: F) -> OrderResult<Option<T>>
33    where
34        F: FnMut(&T, &T) -> Option<Ordering>,
35        Self: Sized,
36    {
37        self.try_select_by(compare, Ordering::Greater)
38    }
39    /// `PartialOrd` version for [`Iterator::min_by_key`].
40    #[inline]
41    fn try_min_by_key<K, F>(self, f: F) -> OrderResult<Option<T>>
42    where
43        F: FnMut(&T) -> Option<K>,
44        K: PartialOrd<K>,
45        Self: Sized,
46    {
47        let mut fk = f;
48        self.try_select_by(|a, b| fk(a).partial_cmp(&fk(b)), Ordering::Greater)
49    }
50    /// `PartialOrd` version for [`Iterator::max`].
51    #[inline]
52    fn try_max(self) -> OrderResult<Option<T>>
53    where
54        T: PartialOrd<T>,
55        Self: Sized,
56    {
57        self.try_select_by(|a, b| a.partial_cmp(b), Ordering::Less)
58    }
59    /// `PartialOrd` version for [`Iterator::max_by`].
60    #[inline]
61    fn try_max_by<F>(self, compare: F) -> OrderResult<Option<T>>
62    where
63        F: FnMut(&T, &T) -> Option<Ordering>,
64        Self: Sized,
65    {
66        self.try_select_by(compare, Ordering::Less)
67    }
68    /// `PartialOrd` version for [`Iterator::max_by_key`].
69    #[inline]
70    fn try_max_by_key<K, F>(self, f: F) -> OrderResult<Option<T>>
71    where
72        F: FnMut(&T) -> Option<K>,
73        K: PartialOrd<K>,
74        Self: Sized,
75    {
76        let mut fk = f;
77        self.try_select_by(|a, b| fk(a).partial_cmp(&fk(b)), Ordering::Less)
78    }
79    /// Base method for getting min or max. `target` is to tell what you want to get is min or max.
80    /// - min -> [`Ordering::Greater`]
81    /// - max -> [`Ordering::Less`]
82    fn try_select_by<F>(self, compare: F, target: Ordering) -> OrderResult<Option<T>>
83    where
84        F: FnMut(&T, &T) -> Option<Ordering>;
85}
86
87impl<T, Iter> TryMinMax<T> for Iter
88where
89    Iter: IntoIterator<Item = T>,
90{
91    #[inline]
92    fn try_select_by<F>(self, compare: F, target: Ordering) -> OrderResult<Option<T>>
93    where
94        F: FnMut(&T, &T) -> Option<Ordering>,
95    {
96        try_select_by(self.into_iter(), compare, target)
97    }
98}
99
100fn try_select_by<T, F>(
101    mut iter: impl Iterator<Item = T>,
102    compare: F,
103    target: Ordering,
104) -> OrderResult<Option<T>>
105where
106    F: FnMut(&T, &T) -> Option<Ordering>,
107{
108    let mut compare = compare;
109    if let Some(first) = iter.next() {
110        match iter.try_fold(first, |a, b| {
111            Some(if compare(&a, &b)? == target { b } else { a })
112        }) {
113            None => Err(InvalidOrderError),
114            x => Ok(x), //some
115        }
116    } else {
117        Ok(None)
118    }
119}
120
121#[cfg(test)]
122#[cfg(feature = "std")]
123mod tests {
124    use crate::*;
125    use rand::distributions::Standard;
126    use rand::prelude::*;
127    use std::vec::Vec;
128
129    #[test]
130    fn try_min_ok() {
131        let rng = thread_rng();
132        let v: Vec<f32> = Standard.sample_iter(rng).take(100).collect();
133        let min = v.iter().try_min().unwrap();
134        assert_eq!(min, v.iter().min_by(|a, b| a.partial_cmp(b).unwrap()));
135        let min = v.iter().try_min().unwrap();
136        assert_eq!(
137            min.as_deref(),
138            v.iter().min_by(|a, b| a.partial_cmp(b).unwrap())
139        );
140
141        let iter = &mut v.iter();
142        let min = iter.try_min().unwrap();
143        assert_eq!(min, v.iter().min_by(|a, b| a.partial_cmp(b).unwrap()));
144    }
145
146    #[test]
147    fn try_min_error() {
148        let rng = thread_rng();
149        let mut v: Vec<f32> = Standard.sample_iter(rng).take(100).collect();
150        v.push(f32::NAN);
151        let min = v.iter().try_min();
152        assert!(min.is_err());
153    }
154}