New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add {min,max}_set(_by{_key)?)? functions #323
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`. | ||
pub fn min_set_impl<I, K, F, L>(mut it: I, | ||
mut key_for: F, | ||
mut lt: L) -> Option<Vec<I::Item>> | ||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to replace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That should work nicely. As it is, I essentially copied the structure from the I'm happy to make the change, but will probably not have the time to do so in the next couple of weeks (currently on vacation). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see. That didn't occur to me when I looked at Maybe I should go back and have a look at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had a look at |
||
result.clear(); | ||
phimuemue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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<I, K, F, L>(it: I, | ||
phimuemue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
key_for: F, | ||
mut lt: L) -> Option<Vec<I::Item>> | ||
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)) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,6 +168,8 @@ mod combinations; | |
mod combinations_with_replacement; | ||
mod exactly_one_err; | ||
mod diff; | ||
#[cfg(feature = "use_std")] | ||
mod extrema_set; | ||
mod format; | ||
#[cfg(feature = "use_std")] | ||
mod group_map; | ||
|
@@ -2142,6 +2144,194 @@ pub trait Itertools : Iterator { | |
group_map::into_group_map(self) | ||
} | ||
|
||
/// 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be honest, I often run into situations where I like to simply compare floats. But most of the time, I am happy that Rust tells me that
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, floats not being In this case, I again copied the style and wording from |
||
/// if an element is NaN. | ||
#[cfg(feature = "use_std")] | ||
fn min_set(self) -> Option<Vec<Self::Item>> | ||
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<F>(self, mut compare: F) -> Option<Vec<Self::Item>> | ||
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<K, F>(self, key: F) -> Option<Vec<Self::Item>> | ||
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<Vec<Self::Item>> | ||
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<F>(self, mut compare: F) -> Option<Vec<Self::Item>> | ||
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<K, F>(self, key: F) -> Option<Vec<Self::Item>> | ||
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: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about returning an empty
Vec
instead ofNone
? I think it would make things a bit more clear, especially since the types do not indicate that aSome
always contains a non-empty vector.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we could/should be a bit more generic than
Vec
here? I.e. should we try to be likecollect
(which returns somethingFromIterator<Self::Item>
and lets the caller choose)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think that returning an Option is a good choice. In application code that I write, when using max or min functionality of sequences, an empty sequence is often an indication that something has gone awry and should be forced to be handled in another way. I imagine that in most cases, the Option-ness can be handled by the
?
-operator. Also, it matches the signature for max/min from the standard library, which returns anOption
of anItem
. I can naturally change it, but I do think that Option makes sense here.As for why Vec and not some kind of iterator. The number of results is unknown before computing the result, and the result is unknown before going through the whole input iterator, which implies that some kind of temporary storage area is needed. Since the data is already in a Vec, I thought that the least convoluted thing was to just give the user back that. We can of course wrap the already constructed
Vec
in something that exposes it like aFromIterator
, I have no real opinion here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the
Vec
: I naively thought about substituting the wholeVec
even within the implementation (basically allowing to customize the kind of temporary storage), so that no conversion would be required upon returning it.However, I see that
FromIterator
alone is possibly a bit too narrow to acchieve that. Maybe any container satisfyingDefault + Extend
orFromIterator + Extend
could be used?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the code by neccesity (unless double iteration is used) provisionally accumulates an unbounded number of result items, and discards these when a new provisionally best results is found, something more than
FromIterator + Extend
would be needed AFAIK.My guess would be that anything that also has the possibility to clear the results is no better than the interal
Vec
, and would also be less clear. I'd be glad to learn I'm wrong though!