Skip to content

Commit 1cb2c36

Browse files
bors[bot]zayenzphimuemue
authored
Merge #613
613: Minmax set (prev "Add {min,max}_set(_by{_key)?)? functions") r=jswrenn a=phimuemue This PR supersedes #323 (I did not know how I could amend to the original PR, so sorry for the "duplicate" PR here.) The original PR by `@zayenz` looked rather good. I adjusted the stuff that came up during discussion back then: * Teturn type `Vec` instead of `Option` - emptiness is sufficiently well represented by `Vec`. * Functions require `Ord` instead of `PartialOrd` - just as `Iterator::min`. * Avoid duplicate calls to `lt` by accepting a `FnMut(...)->Ordering` - seems canonical compared to the `bool`-solution. * Use internal iteration instead of a manual `for`-loop. Moreover, I simplified some bits. Co-authored-by: Mikael Zayenz Lagerkvist <zayenz@gmail.com> Co-authored-by: philipp <descpl@yahoo.de>
2 parents a96ef76 + 846219f commit 1cb2c36

File tree

4 files changed

+346
-1
lines changed

4 files changed

+346
-1
lines changed

src/extrema_set.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use std::cmp::Ordering;
2+
3+
/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
4+
pub fn min_set_impl<I, K, F, Compare>(
5+
mut it: I,
6+
mut key_for: F,
7+
mut compare: Compare,
8+
) -> Vec<I::Item>
9+
where
10+
I: Iterator,
11+
F: FnMut(&I::Item) -> K,
12+
Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
13+
{
14+
match it.next() {
15+
None => Vec::new(),
16+
Some(element) => {
17+
let mut current_key = key_for(&element);
18+
let mut result = vec![element];
19+
it.for_each(|element| {
20+
let key = key_for(&element);
21+
match compare(&element, &result[0], &key, &current_key) {
22+
Ordering::Less => {
23+
result.clear();
24+
result.push(element);
25+
current_key = key;
26+
}
27+
Ordering::Equal => {
28+
result.push(element);
29+
}
30+
Ordering::Greater => {}
31+
}
32+
});
33+
result
34+
}
35+
}
36+
}
37+
38+
/// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`.
39+
pub fn max_set_impl<I, K, F, Compare>(it: I, key_for: F, mut compare: Compare) -> Vec<I::Item>
40+
where
41+
I: Iterator,
42+
F: FnMut(&I::Item) -> K,
43+
Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
44+
{
45+
min_set_impl(it, key_for, |it1, it2, key1, key2| {
46+
compare(it2, it1, key2, key1)
47+
})
48+
}

src/lib.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ mod combinations_with_replacement;
197197
mod exactly_one_err;
198198
mod diff;
199199
mod flatten_ok;
200+
#[cfg(feature = "use_std")]
201+
mod extrema_set;
200202
mod format;
201203
#[cfg(feature = "use_std")]
202204
mod grouping_map;
@@ -2902,6 +2904,194 @@ pub trait Itertools : Iterator {
29022904
grouping_map::new(grouping_map::MapForGrouping::new(self, key_mapper))
29032905
}
29042906

2907+
/// Return all minimum elements of an iterator.
2908+
///
2909+
/// # Examples
2910+
///
2911+
/// ```
2912+
/// use itertools::Itertools;
2913+
///
2914+
/// let a: [i32; 0] = [];
2915+
/// assert_eq!(a.iter().min_set(), Vec::<&i32>::new());
2916+
///
2917+
/// let a = [1];
2918+
/// assert_eq!(a.iter().min_set(), vec![&1]);
2919+
///
2920+
/// let a = [1, 2, 3, 4, 5];
2921+
/// assert_eq!(a.iter().min_set(), vec![&1]);
2922+
///
2923+
/// let a = [1, 1, 1, 1];
2924+
/// assert_eq!(a.iter().min_set(), vec![&1, &1, &1, &1]);
2925+
/// ```
2926+
///
2927+
/// The elements can be floats but no particular result is guaranteed
2928+
/// if an element is NaN.
2929+
#[cfg(feature = "use_std")]
2930+
fn min_set(self) -> Vec<Self::Item>
2931+
where Self: Sized, Self::Item: Ord
2932+
{
2933+
extrema_set::min_set_impl(self, |_| (), |x, y, _, _| x.cmp(y))
2934+
}
2935+
2936+
/// Return all minimum elements of an iterator, as determined by
2937+
/// the specified function.
2938+
///
2939+
/// # Examples
2940+
///
2941+
/// ```
2942+
/// # use std::cmp::Ordering;
2943+
/// use itertools::Itertools;
2944+
///
2945+
/// let a: [(i32, i32); 0] = [];
2946+
/// assert_eq!(a.iter().min_set_by(|_, _| Ordering::Equal), Vec::<&(i32, i32)>::new());
2947+
///
2948+
/// let a = [(1, 2)];
2949+
/// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2)]);
2950+
///
2951+
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
2952+
/// assert_eq!(a.iter().min_set_by(|&&(_,k1), &&(_,k2)| k1.cmp(&k2)), vec![&(1, 2), &(2, 2)]);
2953+
///
2954+
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
2955+
/// assert_eq!(a.iter().min_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]);
2956+
/// ```
2957+
///
2958+
/// The elements can be floats but no particular result is guaranteed
2959+
/// if an element is NaN.
2960+
#[cfg(feature = "use_std")]
2961+
fn min_set_by<F>(self, mut compare: F) -> Vec<Self::Item>
2962+
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
2963+
{
2964+
extrema_set::min_set_impl(
2965+
self,
2966+
|_| (),
2967+
|x, y, _, _| compare(x, y)
2968+
)
2969+
}
2970+
2971+
/// Return all minimum elements of an iterator, as determined by
2972+
/// the specified function.
2973+
///
2974+
/// # Examples
2975+
///
2976+
/// ```
2977+
/// use itertools::Itertools;
2978+
///
2979+
/// let a: [(i32, i32); 0] = [];
2980+
/// assert_eq!(a.iter().min_set_by_key(|_| ()), Vec::<&(i32, i32)>::new());
2981+
///
2982+
/// let a = [(1, 2)];
2983+
/// assert_eq!(a.iter().min_set_by_key(|&&(k,_)| k), vec![&(1, 2)]);
2984+
///
2985+
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
2986+
/// assert_eq!(a.iter().min_set_by_key(|&&(_, k)| k), vec![&(1, 2), &(2, 2)]);
2987+
///
2988+
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
2989+
/// assert_eq!(a.iter().min_set_by_key(|&&(k, _)| k), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]);
2990+
/// ```
2991+
///
2992+
/// The elements can be floats but no particular result is guaranteed
2993+
/// if an element is NaN.
2994+
#[cfg(feature = "use_std")]
2995+
fn min_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
2996+
where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
2997+
{
2998+
extrema_set::min_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
2999+
}
3000+
3001+
/// Return all maximum elements of an iterator.
3002+
///
3003+
/// # Examples
3004+
///
3005+
/// ```
3006+
/// use itertools::Itertools;
3007+
///
3008+
/// let a: [i32; 0] = [];
3009+
/// assert_eq!(a.iter().max_set(), Vec::<&i32>::new());
3010+
///
3011+
/// let a = [1];
3012+
/// assert_eq!(a.iter().max_set(), vec![&1]);
3013+
///
3014+
/// let a = [1, 2, 3, 4, 5];
3015+
/// assert_eq!(a.iter().max_set(), vec![&5]);
3016+
///
3017+
/// let a = [1, 1, 1, 1];
3018+
/// assert_eq!(a.iter().max_set(), vec![&1, &1, &1, &1]);
3019+
/// ```
3020+
///
3021+
/// The elements can be floats but no particular result is guaranteed
3022+
/// if an element is NaN.
3023+
#[cfg(feature = "use_std")]
3024+
fn max_set(self) -> Vec<Self::Item>
3025+
where Self: Sized, Self::Item: Ord
3026+
{
3027+
extrema_set::max_set_impl(self, |_| (), |x, y, _, _| x.cmp(y))
3028+
}
3029+
3030+
/// Return all maximum elements of an iterator, as determined by
3031+
/// the specified function.
3032+
///
3033+
/// # Examples
3034+
///
3035+
/// ```
3036+
/// # use std::cmp::Ordering;
3037+
/// use itertools::Itertools;
3038+
///
3039+
/// let a: [(i32, i32); 0] = [];
3040+
/// assert_eq!(a.iter().max_set_by(|_, _| Ordering::Equal), Vec::<&(i32, i32)>::new());
3041+
///
3042+
/// let a = [(1, 2)];
3043+
/// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2)]);
3044+
///
3045+
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
3046+
/// assert_eq!(a.iter().max_set_by(|&&(_,k1), &&(_,k2)| k1.cmp(&k2)), vec![&(3, 9), &(5, 9)]);
3047+
///
3048+
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
3049+
/// assert_eq!(a.iter().max_set_by(|&&(k1,_), &&(k2, _)| k1.cmp(&k2)), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]);
3050+
/// ```
3051+
///
3052+
/// The elements can be floats but no particular result is guaranteed
3053+
/// if an element is NaN.
3054+
#[cfg(feature = "use_std")]
3055+
fn max_set_by<F>(self, mut compare: F) -> Vec<Self::Item>
3056+
where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering
3057+
{
3058+
extrema_set::max_set_impl(
3059+
self,
3060+
|_| (),
3061+
|x, y, _, _| compare(x, y)
3062+
)
3063+
}
3064+
3065+
/// Return all minimum elements of an iterator, as determined by
3066+
/// the specified function.
3067+
///
3068+
/// # Examples
3069+
///
3070+
/// ```
3071+
/// use itertools::Itertools;
3072+
///
3073+
/// let a: [(i32, i32); 0] = [];
3074+
/// assert_eq!(a.iter().max_set_by_key(|_| ()), Vec::<&(i32, i32)>::new());
3075+
///
3076+
/// let a = [(1, 2)];
3077+
/// assert_eq!(a.iter().max_set_by_key(|&&(k,_)| k), vec![&(1, 2)]);
3078+
///
3079+
/// let a = [(1, 2), (2, 2), (3, 9), (4, 8), (5, 9)];
3080+
/// assert_eq!(a.iter().max_set_by_key(|&&(_, k)| k), vec![&(3, 9), &(5, 9)]);
3081+
///
3082+
/// let a = [(1, 2), (1, 3), (1, 4), (1, 5)];
3083+
/// assert_eq!(a.iter().max_set_by_key(|&&(k, _)| k), vec![&(1, 2), &(1, 3), &(1, 4), &(1, 5)]);
3084+
/// ```
3085+
///
3086+
/// The elements can be floats but no particular result is guaranteed
3087+
/// if an element is NaN.
3088+
#[cfg(feature = "use_std")]
3089+
fn max_set_by_key<K, F>(self, key: F) -> Vec<Self::Item>
3090+
where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K
3091+
{
3092+
extrema_set::max_set_impl(self, key, |_, _, kx, ky| kx.cmp(ky))
3093+
}
3094+
29053095
/// Return the minimum and maximum elements in the iterator.
29063096
///
29073097
/// The return type `MinMaxResult` is an enum of three variants:

tests/quick.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,3 +1693,62 @@ quickcheck! {
16931693
}
16941694
}
16951695

1696+
quickcheck! {
1697+
fn min_set_contains_min(a: Vec<(usize, char)>) -> bool {
1698+
let result_set = a.iter().min_set();
1699+
if let Some(result_element) = a.iter().min() {
1700+
result_set.contains(&result_element)
1701+
} else {
1702+
result_set.is_empty()
1703+
}
1704+
}
1705+
1706+
fn min_set_by_contains_min(a: Vec<(usize, char)>) -> bool {
1707+
let compare = |x: &&(usize, char), y: &&(usize, char)| x.1.cmp(&y.1);
1708+
let result_set = a.iter().min_set_by(compare);
1709+
if let Some(result_element) = a.iter().min_by(compare) {
1710+
result_set.contains(&result_element)
1711+
} else {
1712+
result_set.is_empty()
1713+
}
1714+
}
1715+
1716+
fn min_set_by_key_contains_min(a: Vec<(usize, char)>) -> bool {
1717+
let key = |x: &&(usize, char)| x.1;
1718+
let result_set = a.iter().min_set_by_key(&key);
1719+
if let Some(result_element) = a.iter().min_by_key(&key) {
1720+
result_set.contains(&result_element)
1721+
} else {
1722+
result_set.is_empty()
1723+
}
1724+
}
1725+
1726+
fn max_set_contains_max(a: Vec<(usize, char)>) -> bool {
1727+
let result_set = a.iter().max_set();
1728+
if let Some(result_element) = a.iter().max() {
1729+
result_set.contains(&result_element)
1730+
} else {
1731+
result_set.is_empty()
1732+
}
1733+
}
1734+
1735+
fn max_set_by_contains_max(a: Vec<(usize, char)>) -> bool {
1736+
let compare = |x: &&(usize, char), y: &&(usize, char)| x.1.cmp(&y.1);
1737+
let result_set = a.iter().max_set_by(compare);
1738+
if let Some(result_element) = a.iter().max_by(compare) {
1739+
result_set.contains(&result_element)
1740+
} else {
1741+
result_set.is_empty()
1742+
}
1743+
}
1744+
1745+
fn max_set_by_key_contains_max(a: Vec<(usize, char)>) -> bool {
1746+
let key = |x: &&(usize, char)| x.1;
1747+
let result_set = a.iter().max_set_by_key(&key);
1748+
if let Some(result_element) = a.iter().max_by_key(&key) {
1749+
result_set.contains(&result_element)
1750+
} else {
1751+
result_set.is_empty()
1752+
}
1753+
}
1754+
}

tests/test_std.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,54 @@ fn diff_shorter() {
992992
});
993993
}
994994

995+
#[test]
996+
fn extrema_set() {
997+
use std::cmp::Ordering;
998+
999+
// A peculiar type: Equality compares both tuple items, but ordering only the
1000+
// first item. Used to distinguish equal elements.
1001+
#[derive(Clone, Debug, PartialEq, Eq)]
1002+
struct Val(u32, u32);
1003+
1004+
impl PartialOrd<Val> for Val {
1005+
fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
1006+
self.0.partial_cmp(&other.0)
1007+
}
1008+
}
1009+
1010+
impl Ord for Val {
1011+
fn cmp(&self, other: &Val) -> Ordering {
1012+
self.0.cmp(&other.0)
1013+
}
1014+
}
1015+
1016+
assert_eq!(None::<u32>.iter().min_set(), Vec::<&u32>::new());
1017+
assert_eq!(None::<u32>.iter().max_set(), Vec::<&u32>::new());
1018+
1019+
assert_eq!(Some(1u32).iter().min_set(), vec![&1]);
1020+
assert_eq!(Some(1u32).iter().max_set(), vec![&1]);
1021+
1022+
let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)];
1023+
1024+
let min_set = data.iter().min_set();
1025+
assert_eq!(min_set, vec![&Val(0, 1), &Val(0, 2)]);
1026+
1027+
let min_set_by_key = data.iter().min_set_by_key(|v| v.1);
1028+
assert_eq!(min_set_by_key, vec![&Val(2, 0), &Val(1, 0)]);
1029+
1030+
let min_set_by = data.iter().min_set_by(|x, y| x.1.cmp(&y.1));
1031+
assert_eq!(min_set_by, vec![&Val(2, 0), &Val(1, 0)]);
1032+
1033+
let max_set = data.iter().max_set();
1034+
assert_eq!(max_set, vec![&Val(2, 0), &Val(2, 1)]);
1035+
1036+
let max_set_by_key = data.iter().max_set_by_key(|v| v.1);
1037+
assert_eq!(max_set_by_key, vec![&Val(0, 2)]);
1038+
1039+
let max_set_by = data.iter().max_set_by(|x, y| x.1.cmp(&y.1));
1040+
assert_eq!(max_set_by, vec![&Val(0, 2)]);
1041+
}
1042+
9951043
#[test]
9961044
fn minmax() {
9971045
use std::cmp::Ordering;
@@ -1119,4 +1167,4 @@ fn multiunzip() {
11191167
let (): () = [(), (), ()].iter().cloned().multiunzip();
11201168
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();
11211169
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]));
1122-
}
1170+
}

0 commit comments

Comments
 (0)