Skip to content

Add max_by #379

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

Closed
wants to merge 9 commits into from
48 changes: 34 additions & 14 deletions src/stream/stream/min_by.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,41 @@ use crate::task::{Context, Poll};
pin_project! {
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct MinByFuture<S, F, T> {
pub struct MinMaxByFuture<S, F, T> {
#[pin]
stream: S,
compare: F,
min: Option<T>,
value: Option<T>,
direction: Direction,
}
}

impl<S, F, T> MinByFuture<S, F, T> {
pub(super) fn new(stream: S, compare: F) -> Self {
MinByFuture {
#[derive(PartialEq, Eq)]
enum Direction {
Maximizing,
Minimizing,
}

impl<S, F, T> MinMaxByFuture<S, F, T> {
pub(super) fn new_min(stream: S, compare: F) -> Self {
MinMaxByFuture::new(stream, compare, Direction::Minimizing)
}

pub(super) fn new_max(stream: S, compare: F) -> Self {
MinMaxByFuture::new(stream, compare, Direction::Maximizing)
}

fn new(stream: S, compare: F, direction: Direction) -> Self {
MinMaxByFuture {
stream,
compare,
min: None,
value: None,
direction,
}
}
}

impl<S, F> Future for MinByFuture<S, F, S::Item>
impl<S, F> Future for MinMaxByFuture<S, F, S::Item>
where
S: Stream + Unpin + Sized,
S::Item: Copy,
Expand All @@ -37,22 +53,26 @@ where
type Output = Option<S::Item>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
use Direction::*;
use Ordering::*;

let this = self.project();
let next = futures_core::ready!(this.stream.poll_next(cx));

match next {
Some(new) => {
cx.waker().wake_by_ref();
match this.min.take() {
None => *this.min = Some(new),
Some(old) => match (this.compare)(&new, &old) {
Ordering::Less => *this.min = Some(new),
_ => *this.min = Some(old),

match this.value.take() {
None => this.value.replace(new),
Some(old) => match ((this.compare)(&new, &old), this.direction) {
(Less, Minimizing) | (Greater, Maximizing) => this.value.replace(new),
_ => this.value.replace(old),
},
}
};
Poll::Pending
}
None => Poll::Ready(*this.min),
None => Poll::Ready(*this.value),
}
}
}
45 changes: 42 additions & 3 deletions src/stream/stream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ use gt::GtFuture;
use last::LastFuture;
use le::LeFuture;
use lt::LtFuture;
use min_by::MinByFuture;
use min_by::MinMaxByFuture;
use next::NextFuture;
use nth::NthFuture;
use partial_cmp::PartialCmpFuture;
Expand Down Expand Up @@ -600,6 +600,45 @@ extension_trait! {
FilterMap::new(self, f)
}

#[doc = r#"
Returns the element that gives the maximum value with respect to the
specified comparison function. If several elements are equally maximum,
the first element is returned. If the stream is empty, `None` is returned.

# Examples

```
# fn main() { async_std::task::block_on(async {
#
use std::collections::VecDeque;

use async_std::prelude::*;

let s: VecDeque<usize> = vec![1, 2, 3].into_iter().collect();

let min = s.clone().max_by(|x, y| x.cmp(y)).await;
assert_eq!(min, Some(3));

let min = s.max_by(|x, y| y.cmp(x)).await;
assert_eq!(min, Some(1));

let min = VecDeque::<usize>::new().max_by(|x, y| x.cmp(y)).await;
assert_eq!(min, None);
#
# }) }
```
"#]
fn max_by<F>(
self,
compare: F,
) -> impl Future<Output = Option<Self::Item>> [MinMaxByFuture<Self, F, Self::Item>]
where
Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Ordering,
{
MinMaxByFuture::new_max(self, compare)
}

#[doc = r#"
Returns the element that gives the minimum value with respect to the
specified comparison function. If several elements are equally minimum,
Expand Down Expand Up @@ -631,12 +670,12 @@ extension_trait! {
fn min_by<F>(
self,
compare: F,
) -> impl Future<Output = Option<Self::Item>> [MinByFuture<Self, F, Self::Item>]
) -> impl Future<Output = Option<Self::Item>> [MinMaxByFuture<Self, F, Self::Item>]
where
Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Ordering,
{
MinByFuture::new(self, compare)
MinMaxByFuture::new_min(self, compare)
}

#[doc = r#"
Expand Down