Skip to content

Commit 7c96136

Browse files
bors[bot]tspiteri
andauthored
Merge #412
412: Position max, min and minmax methods r=jswrenn a=tspiteri The following nine methods are provided: * `position_max`, `position_max_by_key`, `position_max_by`; return `Option<usize>` * `position_min`, `position_min_by_key`, `position_min_by`; return `Option<usize>` * `position_minmax`, `position_minmax_by_key`, `position_minmax_by`; return `MinMaxResult<usize>` The `position_max` and `position_min` methods are implemented using `Iterator::enumerate` and `Iterator::max_by`/`min_by`. The `position_minmax` methods are implemented using `Iterator::enumerate` and `minmax::minmax_impl` rather than `Itertools::minmax_by`, so that the comparison constraint can be relaxed from `Ord` to `PartialOrd`, just like for the `minmax` methods. Closes #410. Co-authored-by: Trevor Spiteri <tspiteri@ieee.org>
2 parents 65bce06 + cb66dd8 commit 7c96136

File tree

1 file changed

+310
-0
lines changed

1 file changed

+310
-0
lines changed

src/lib.rs

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2254,6 +2254,316 @@ pub trait Itertools : Iterator {
22542254
)
22552255
}
22562256

2257+
/// Return the position of the maximum element in the iterator.
2258+
///
2259+
/// If several elements are equally maximum, the position of the
2260+
/// last of them is returned.
2261+
///
2262+
/// # Examples
2263+
///
2264+
/// ```
2265+
/// use itertools::Itertools;
2266+
///
2267+
/// let a: [i32; 0] = [];
2268+
/// assert_eq!(a.iter().position_max(), None);
2269+
///
2270+
/// let a = [-3, 0, 1, 5, -10];
2271+
/// assert_eq!(a.iter().position_max(), Some(3));
2272+
///
2273+
/// let a = [1, 1, -1, -1];
2274+
/// assert_eq!(a.iter().position_max(), Some(1));
2275+
/// ```
2276+
fn position_max(self) -> Option<usize>
2277+
where Self: Sized, Self::Item: Ord
2278+
{
2279+
self.enumerate()
2280+
.max_by(|x, y| Ord::cmp(&x.1, &y.1))
2281+
.map(|x| x.0)
2282+
}
2283+
2284+
/// Return the position of the maximum element in the iterator, as
2285+
/// determined by the specified function.
2286+
///
2287+
/// If several elements are equally maximum, the position of the
2288+
/// last of them is returned.
2289+
///
2290+
/// # Examples
2291+
///
2292+
/// ```
2293+
/// use itertools::Itertools;
2294+
///
2295+
/// let a: [i32; 0] = [];
2296+
/// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), None);
2297+
///
2298+
/// let a = [-3_i32, 0, 1, 5, -10];
2299+
/// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(4));
2300+
///
2301+
/// let a = [1_i32, 1, -1, -1];
2302+
/// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3));
2303+
/// ```
2304+
fn position_max_by_key<K, F>(self, mut key: F) -> Option<usize>
2305+
where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
2306+
{
2307+
self.enumerate()
2308+
.max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1)))
2309+
.map(|x| x.0)
2310+
}
2311+
2312+
/// Return the position of the maximum element in the iterator, as
2313+
/// determined by the specified comparison function.
2314+
///
2315+
/// If several elements are equally maximum, the position of the
2316+
/// last of them is returned.
2317+
///
2318+
/// # Examples
2319+
///
2320+
/// ```
2321+
/// use itertools::Itertools;
2322+
///
2323+
/// let a: [i32; 0] = [];
2324+
/// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), None);
2325+
///
2326+
/// let a = [-3_i32, 0, 1, 5, -10];
2327+
/// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(3));
2328+
///
2329+
/// let a = [1_i32, 1, -1, -1];
2330+
/// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1));
2331+
/// ```
2332+
fn position_max_by<F>(self, mut compare: F) -> Option<usize>
2333+
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
2334+
{
2335+
self.enumerate()
2336+
.max_by(|x, y| compare(&x.1, &y.1))
2337+
.map(|x| x.0)
2338+
}
2339+
2340+
/// Return the position of the minimum element in the iterator.
2341+
///
2342+
/// If several elements are equally minimum, the position of the
2343+
/// first of them is returned.
2344+
///
2345+
/// # Examples
2346+
///
2347+
/// ```
2348+
/// use itertools::Itertools;
2349+
///
2350+
/// let a: [i32; 0] = [];
2351+
/// assert_eq!(a.iter().position_min(), None);
2352+
///
2353+
/// let a = [-3, 0, 1, 5, -10];
2354+
/// assert_eq!(a.iter().position_min(), Some(4));
2355+
///
2356+
/// let a = [1, 1, -1, -1];
2357+
/// assert_eq!(a.iter().position_min(), Some(2));
2358+
/// ```
2359+
fn position_min(self) -> Option<usize>
2360+
where Self: Sized, Self::Item: Ord
2361+
{
2362+
self.enumerate()
2363+
.min_by(|x, y| Ord::cmp(&x.1, &y.1))
2364+
.map(|x| x.0)
2365+
}
2366+
2367+
/// Return the position of the minimum element in the iterator, as
2368+
/// determined by the specified function.
2369+
///
2370+
/// If several elements are equally minimum, the position of the
2371+
/// first of them is returned.
2372+
///
2373+
/// # Examples
2374+
///
2375+
/// ```
2376+
/// use itertools::Itertools;
2377+
///
2378+
/// let a: [i32; 0] = [];
2379+
/// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), None);
2380+
///
2381+
/// let a = [-3_i32, 0, 1, 5, -10];
2382+
/// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(1));
2383+
///
2384+
/// let a = [1_i32, 1, -1, -1];
2385+
/// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0));
2386+
/// ```
2387+
fn position_min_by_key<K, F>(self, mut key: F) -> Option<usize>
2388+
where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
2389+
{
2390+
self.enumerate()
2391+
.min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1)))
2392+
.map(|x| x.0)
2393+
}
2394+
2395+
/// Return the position of the minimum element in the iterator, as
2396+
/// determined by the specified comparison function.
2397+
///
2398+
/// If several elements are equally minimum, the position of the
2399+
/// first of them is returned.
2400+
///
2401+
/// # Examples
2402+
///
2403+
/// ```
2404+
/// use itertools::Itertools;
2405+
///
2406+
/// let a: [i32; 0] = [];
2407+
/// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), None);
2408+
///
2409+
/// let a = [-3_i32, 0, 1, 5, -10];
2410+
/// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(4));
2411+
///
2412+
/// let a = [1_i32, 1, -1, -1];
2413+
/// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2));
2414+
/// ```
2415+
fn position_min_by<F>(self, mut compare: F) -> Option<usize>
2416+
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
2417+
{
2418+
self.enumerate()
2419+
.min_by(|x, y| compare(&x.1, &y.1))
2420+
.map(|x| x.0)
2421+
}
2422+
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 self.enumerate().minmax_by_key(|e| key(&e.1)) {
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 self.enumerate().minmax_by(|x, y| compare(&x.1, &y.1)) {
2561+
NoElements => NoElements,
2562+
OneElement(x) => OneElement(x.0),
2563+
MinMax(x, y) => MinMax(x.0, y.0),
2564+
}
2565+
}
2566+
22572567
/// If the iterator yields exactly one element, that element will be returned, otherwise
22582568
/// an error will be returned containing an iterator that has the same output as the input
22592569
/// iterator.

0 commit comments

Comments
 (0)