Skip to content

Commit 8a5fe0b

Browse files
committed
Add position_{minmax, minmax_by_key, minmax_by}
The implementation uses minmax::minmax_impl rather than Itertools::minmax_by, so that the comparison constraint is relaxed to PartialOrd, just like for the minmax functions.
1 parent c5bfe23 commit 8a5fe0b

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed

src/lib.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,6 +2420,154 @@ pub trait Itertools : Iterator {
24202420
.map(|x| x.0)
24212421
}
24222422

2423+
/// Return the positions of the minimum and maximum elements in
2424+
/// the iterator.
2425+
///
2426+
/// The return type [`MinMaxResult`] is an enum of three variants:
2427+
///
2428+
/// - `NoElements` if the iterator is empty.
2429+
/// - `OneElement(xpos)` if the iterator has exactly one element.
2430+
/// - `MinMax(xpos, ypos)` is returned otherwise, where the
2431+
/// element at `xpos` ≤ the element at `ypos`. While the
2432+
/// referenced elements themselves may be equal, `xpos` cannot
2433+
/// be equal to `ypos`.
2434+
///
2435+
/// On an iterator of length `n`, `position_minmax` does `1.5 * n`
2436+
/// comparisons, and so is faster than calling `positon_min` and
2437+
/// `position_max` separately which does `2 * n` comparisons.
2438+
///
2439+
/// For the minimum, if several elements are equally minimum, the
2440+
/// position of the first of them is returned. For the maximum, if
2441+
/// several elements are equally maximum, the position of the last
2442+
/// of them is returned.
2443+
///
2444+
/// The elements can be floats but no particular result is
2445+
/// guaranteed if an element is NaN.
2446+
///
2447+
/// # Examples
2448+
///
2449+
/// ```
2450+
/// use itertools::Itertools;
2451+
/// use itertools::MinMaxResult::{NoElements, OneElement, MinMax};
2452+
///
2453+
/// let a: [i32; 0] = [];
2454+
/// assert_eq!(a.iter().position_minmax(), NoElements);
2455+
///
2456+
/// let a = [10];
2457+
/// assert_eq!(a.iter().position_minmax(), OneElement(0));
2458+
///
2459+
/// let a = [-3, 0, 1, 5, -10];
2460+
/// assert_eq!(a.iter().position_minmax(), MinMax(4, 3));
2461+
///
2462+
/// let a = [1, 1, -1, -1];
2463+
/// assert_eq!(a.iter().position_minmax(), MinMax(2, 1));
2464+
/// ```
2465+
///
2466+
/// [`MinMaxResult`]: enum.MinMaxResult.html
2467+
fn position_minmax(self) -> MinMaxResult<usize>
2468+
where Self: Sized, Self::Item: PartialOrd
2469+
{
2470+
use MinMaxResult::{NoElements, OneElement, MinMax};
2471+
match minmax::minmax_impl(self.enumerate(), |_| (), |x, y, _, _| x.1 < y.1) {
2472+
NoElements => NoElements,
2473+
OneElement(x) => OneElement(x.0),
2474+
MinMax(x, y) => MinMax(x.0, y.0),
2475+
}
2476+
}
2477+
2478+
/// Return the postions of the minimum and maximum elements of an
2479+
/// iterator, as determined by the specified function.
2480+
///
2481+
/// The return value is a variant of [`MinMaxResult`] like for
2482+
/// [`position_minmax`].
2483+
///
2484+
/// For the minimum, if several elements are equally minimum, the
2485+
/// position of the first of them is returned. For the maximum, if
2486+
/// several elements are equally maximum, the position of the last
2487+
/// of them is returned.
2488+
///
2489+
/// The keys can be floats but no particular result is guaranteed
2490+
/// if a key is NaN.
2491+
///
2492+
/// # Examples
2493+
///
2494+
/// ```
2495+
/// use itertools::Itertools;
2496+
/// use itertools::MinMaxResult::{NoElements, OneElement, MinMax};
2497+
///
2498+
/// let a: [i32; 0] = [];
2499+
/// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), NoElements);
2500+
///
2501+
/// let a = [10_i32];
2502+
/// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), OneElement(0));
2503+
///
2504+
/// let a = [-3_i32, 0, 1, 5, -10];
2505+
/// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(1, 4));
2506+
///
2507+
/// let a = [1_i32, 1, -1, -1];
2508+
/// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(0, 3));
2509+
/// ```
2510+
///
2511+
/// [`MinMaxResult`]: enum.MinMaxResult.html
2512+
/// [`position_minmax`]: #method.position_minmax
2513+
fn position_minmax_by_key<K, F>(self, mut key: F) -> MinMaxResult<usize>
2514+
where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K
2515+
{
2516+
use MinMaxResult::{NoElements, OneElement, MinMax};
2517+
match minmax::minmax_impl(self.enumerate(), |x| key(&x.1), |_, _, xk, yk| xk < yk) {
2518+
NoElements => NoElements,
2519+
OneElement(x) => OneElement(x.0),
2520+
MinMax(x, y) => MinMax(x.0, y.0),
2521+
}
2522+
}
2523+
2524+
/// Return the postions of the minimum and maximum elements of an
2525+
/// iterator, as determined by the specified comparison function.
2526+
///
2527+
/// The return value is a variant of [`MinMaxResult`] like for
2528+
/// [`position_minmax`].
2529+
///
2530+
/// For the minimum, if several elements are equally minimum, the
2531+
/// position of the first of them is returned. For the maximum, if
2532+
/// several elements are equally maximum, the position of the last
2533+
/// of them is returned.
2534+
///
2535+
/// # Examples
2536+
///
2537+
/// ```
2538+
/// use itertools::Itertools;
2539+
/// use itertools::MinMaxResult::{NoElements, OneElement, MinMax};
2540+
///
2541+
/// let a: [i32; 0] = [];
2542+
/// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), NoElements);
2543+
///
2544+
/// let a = [10_i32];
2545+
/// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), OneElement(0));
2546+
///
2547+
/// let a = [-3_i32, 0, 1, 5, -10];
2548+
/// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(4, 3));
2549+
///
2550+
/// let a = [1_i32, 1, -1, -1];
2551+
/// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(2, 1));
2552+
/// ```
2553+
///
2554+
/// [`MinMaxResult`]: enum.MinMaxResult.html
2555+
/// [`position_minmax`]: #method.position_minmax
2556+
fn position_minmax_by<F>(self, mut compare: F) -> MinMaxResult<usize>
2557+
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
2558+
{
2559+
use MinMaxResult::{NoElements, OneElement, MinMax};
2560+
match minmax::minmax_impl(
2561+
self.enumerate(),
2562+
|_| (),
2563+
|x, y, _, _| Ordering::Less == compare(&x.1, &y.1)
2564+
) {
2565+
NoElements => NoElements,
2566+
OneElement(x) => OneElement(x.0),
2567+
MinMax(x, y) => MinMax(x.0, y.0),
2568+
}
2569+
}
2570+
24232571
/// If the iterator yields exactly one element, that element will be returned, otherwise
24242572
/// an error will be returned containing an iterator that has the same output as the input
24252573
/// iterator.

0 commit comments

Comments
 (0)