From f9a21a474b02640117adca2cf016a26a8a2a4377 Mon Sep 17 00:00:00 2001 From: Alexander Hirsch Date: Tue, 29 Oct 2019 14:56:26 +0100 Subject: [PATCH] Add `try_filter_map_results` method. --- src/adaptors/mod.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 19 +++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index b2956d222..31aee323f 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -1208,6 +1208,71 @@ impl Iterator for TryMapResults } } +/// An iterator adapter to filter and apply a fallible transformation on values within a nested `Result`. +/// +/// See [`.try_filter_map_results()`](../trait.Itertools.html#method.try_filter_map_results) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct TryFilterMapResults { + iter: I, + f: F +} + +/// Create a new `TryFilterMapResults` iterator. +pub fn try_filter_map_results(iter: I, f: F) -> TryFilterMapResults + where I: Iterator>, + F: FnMut(T) -> Result, E> +{ + TryFilterMapResults { + iter: iter, + f: f, + } +} + +impl Iterator for TryFilterMapResults + where I: Iterator>, + F: FnMut(T) -> Result, E> +{ + type Item = Result; + + fn next(&mut self) -> Option { + loop { + match self.iter.next() { + Some(Ok(v)) => { + if let Some(v) = (self.f)(v).transpose() { + return Some(v); + } + }, + Some(Err(e)) => return Some(Err(e)), + None => return None, + } + } + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: Acc, mut fold_f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, v| { + if let Some(v) = v.and_then(&mut f).transpose() { + fold_f(acc, v) + } else { + acc + } + }) + } + + fn collect(self) -> C + where C: FromIterator + { + let mut f = self.f; + self.iter.filter_map(move |v| v.and_then(&mut f).transpose()).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. diff --git a/src/lib.rs b/src/lib.rs index 0c26f6b02..ebfe45eca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,7 @@ pub mod structs { Batching, Step, MapResults, + TryFilterMapResults, TryMapResults, Merge, MergeBy, @@ -654,6 +655,24 @@ pub trait Itertools : Iterator { adaptors::try_map_results(self, f) } + /// Return an iterator adaptor that filters and transforms every + /// `Result::Ok` value with the provided fallible closure. + /// `Result::Err` values in the original iterator are unchanged. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let input = vec![Ok(11), Ok(41), Err(false), Ok(i32::max_value())]; + /// let it = input.into_iter().try_filter_map_results(|i| if i > 20 { Some(i.checked_add(1).ok_or(true)).transpose() } else { Ok(None) }); + /// itertools::assert_equal(it, vec![Ok(42), Err(false), Err(true)]); + /// ``` + fn try_filter_map_results(self, f: F) -> TryFilterMapResults + where Self: Iterator> + Sized, + F: FnMut(T) -> Result, E>, + { + adaptors::try_filter_map_results(self, f) + } + /// Return an iterator adaptor that merges the two base iterators in /// ascending order. If both base iterators are sorted (ascending), the /// result is sorted.