Skip to content

Commit

Permalink
Merge #377 #381
Browse files Browse the repository at this point in the history
377: Add Itertools::filter_{,map_}results r=jswrenn a=gin-ahirsch

I found myself wanting `filter_map_results()` and just implemented `filter_results()` in the same stretch. I hope you find it useful!

I wasn't sure if #236 applies to these new iterators; I just implemented them.

381: Add Itertools::intersperse_with r=jswrenn a=gin-ahirsch

Pretty self-explanatory. Useful for non-clonable elements or otherwise stateful interspersing.
`intersperse()` could be implemented via `intersperse_with()`, allowing the change to be more minimal. I haven't done it currently, but if you don't see any downsides I'd amend this change to do `self.intersperse_with(|| element)` for `intersperse()` instead.

Co-authored-by: Alexander Hirsch <alexander.hirsch@gin.de>
  • Loading branch information
bors[bot] and gin-ahirsch committed Jun 18, 2020
3 parents a517cfc + 3890a93 + 22b1984 commit 636a332
Show file tree
Hide file tree
Showing 3 changed files with 309 additions and 18 deletions.
152 changes: 145 additions & 7 deletions src/adaptors/mod.rs
Expand Up @@ -1086,28 +1086,31 @@ where
I::Item: Into<R>,
{}

/// An iterator adapter to apply a transformation within a nested `Result`.
#[deprecated(note="Use MapOk instead", since="0.10")]
pub type MapResults<I, F> = MapOk<I, F>;

/// An iterator adapter to apply a transformation within a nested `Result::Ok`.
///
/// See [`.map_results()`](../trait.Itertools.html#method.map_results) for more information.
/// See [`.map_ok()`](../trait.Itertools.html#method.map_ok) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct MapResults<I, F> {
pub struct MapOk<I, F> {
iter: I,
f: F
}

/// Create a new `MapResults` iterator.
pub fn map_results<I, F, T, U, E>(iter: I, f: F) -> MapResults<I, F>
/// Create a new `MapOk` iterator.
pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> U,
{
MapResults {
MapOk {
iter,
f,
}
}

impl<I, F, T, U, E> Iterator for MapResults<I, F>
impl<I, F, T, U, E> Iterator for MapOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> U,
{
Expand Down Expand Up @@ -1136,6 +1139,141 @@ impl<I, F, T, U, E> Iterator for MapResults<I, F>
}
}

/// An iterator adapter to filter values within a nested `Result::Ok`.
///
/// See [`.filter_ok()`](../trait.Itertools.html#method.filter_ok) for more information.
#[derive(Clone)]
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct FilterOk<I, F> {
iter: I,
f: F
}

/// Create a new `FilterOk` iterator.
pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(&T) -> bool,
{
FilterOk {
iter,
f,
}
}

impl<I, F, T, E> Iterator for FilterOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(&T) -> bool,
{
type Item = Result<T, E>;

fn next(&mut self) -> Option<Self::Item> {
loop {
match self.iter.next() {
Some(Ok(v)) => {
if (self.f)(&v) {
return Some(Ok(v));
}
},
Some(Err(e)) => return Some(Err(e)),
None => return None,
}
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.iter.size_hint().1)
}

fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
where Fold: FnMut(Acc, Self::Item) -> Acc,
{
let mut f = self.f;
self.iter.filter(|v| {
v.as_ref().map(&mut f).unwrap_or(true)
}).fold(init, fold_f)
}

fn collect<C>(self) -> C
where C: FromIterator<Self::Item>
{
let mut f = self.f;
self.iter.filter(|v| {
v.as_ref().map(&mut f).unwrap_or(true)
}).collect()
}
}

/// An iterator adapter to filter and apply a transformation on values within a nested `Result::Ok`.
///
/// See [`.filter_map_ok()`](../trait.Itertools.html#method.filter_map_ok) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct FilterMapOk<I, F> {
iter: I,
f: F
}

fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> {
match result {
Ok(Some(v)) => Some(Ok(v)),
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}

/// Create a new `FilterOk` iterator.
pub fn filter_map_ok<I, F, T, U, E>(iter: I, f: F) -> FilterMapOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> Option<U>,
{
FilterMapOk {
iter,
f,
}
}

impl<I, F, T, U, E> Iterator for FilterMapOk<I, F>
where I: Iterator<Item = Result<T, E>>,
F: FnMut(T) -> Option<U>,
{
type Item = Result<U, E>;

fn next(&mut self) -> Option<Self::Item> {
loop {
match self.iter.next() {
Some(Ok(v)) => {
if let Some(v) = (self.f)(v) {
return Some(Ok(v));
}
},
Some(Err(e)) => return Some(Err(e)),
None => return None,
}
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.iter.size_hint().1)
}

fn fold<Acc, Fold>(self, init: Acc, fold_f: Fold) -> Acc
where Fold: FnMut(Acc, Self::Item) -> Acc,
{
let mut f = self.f;
self.iter.filter_map(|v| {
transpose_result(v.map(&mut f))
}).fold(init, fold_f)
}

fn collect<C>(self) -> C
where C: FromIterator<Self::Item>
{
let mut f = self.f;
self.iter.filter_map(|v| {
transpose_result(v.map(&mut f))
}).collect()
}
}

/// An iterator adapter to get the positions of each element that matches a predicate.
///
/// See [`.positions()`](../trait.Itertools.html#method.positions) for more information.
Expand Down
83 changes: 80 additions & 3 deletions src/intersperse.rs
@@ -1,7 +1,6 @@
use std::iter::Fuse;
use super::size_hint;

#[derive(Clone)]
/// An iterator adaptor to insert a particular value
/// between each element of the adapted iterator.
///
Expand All @@ -11,7 +10,7 @@ use super::size_hint;
///
/// See [`.intersperse()`](../trait.Itertools.html#method.intersperse) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Intersperse<I>
where I: Iterator
{
Expand Down Expand Up @@ -62,7 +61,7 @@ impl<I> Iterator for Intersperse<I>
Self: Sized, F: FnMut(B, Self::Item) -> B,
{
let mut accum = init;

if let Some(x) = self.peek.take() {
accum = f(accum, x);
}
Expand All @@ -77,3 +76,81 @@ impl<I> Iterator for Intersperse<I>
})
}
}

/// An iterator adaptor to insert a particular value created by a function
/// between each element of the adapted iterator.
///
/// Iterator element type is `I::Item`
///
/// This iterator is *fused*.
///
/// See [`.intersperse_with()`](../trait.Itertools.html#method.intersperse_with) for more information.
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct IntersperseWith<I, ElemF>
where I: Iterator,
ElemF: FnMut() -> I::Item,
{
element: ElemF,
iter: Fuse<I>,
peek: Option<I::Item>,
}

/// Create a new IntersperseWith iterator
pub fn intersperse_with<I, ElemF>(iter: I, elt: ElemF) -> IntersperseWith<I, ElemF>
where I: Iterator,
ElemF: FnMut() -> I::Item
{
let mut iter = iter.fuse();
IntersperseWith {
peek: iter.next(),
iter: iter,
element: elt,
}
}

impl<I, ElemF> Iterator for IntersperseWith<I, ElemF>
where I: Iterator,
ElemF: FnMut() -> I::Item
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
if self.peek.is_some() {
self.peek.take()
} else {
self.peek = self.iter.next();
if self.peek.is_some() {
Some((self.element)())
} else {
None
}
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
// 2 * SH + { 1 or 0 }
let has_peek = self.peek.is_some() as usize;
let sh = self.iter.size_hint();
size_hint::add_scalar(size_hint::add(sh, sh), has_peek)
}

fn fold<B, F>(mut self, init: B, mut f: F) -> B where
Self: Sized, F: FnMut(B, Self::Item) -> B,
{
let mut accum = init;

if let Some(x) = self.peek.take() {
accum = f(accum, x);
}

let element = &mut self.element;

self.iter.fold(accum,
|accum, x| {
let accum = f(accum, (element)());
let accum = f(accum, x);
accum
})
}
}

0 comments on commit 636a332

Please sign in to comment.