From cad46e789edf86a7792b640dfc33302c4b8d503e Mon Sep 17 00:00:00 2001 From: Alexander Hirsch Date: Tue, 29 Oct 2019 13:55:53 +0100 Subject: [PATCH] Add Itertools::filter_map_results --- 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 73d02a75d..9b01f8c82 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -1184,6 +1184,71 @@ impl Iterator for FilterResults } } +/// An iterator adapter to filter and apply a transformation on values within a nested `Result`. +/// +/// See [`.filter_map_results()`](../trait.Itertools.html#method.filter_map_results) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct FilterMapResults { + iter: I, + f: F +} + +/// Create a new `FilterResults` iterator. +pub fn filter_map_results(iter: I, f: F) -> FilterMapResults + where I: Iterator>, + F: FnMut(T) -> Option, +{ + FilterMapResults { + iter: iter, + f: f, + } +} + +impl Iterator for FilterMapResults + where I: Iterator>, + F: FnMut(T) -> Option, +{ + type Item = Result; + + fn next(&mut self) -> Option { + 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) { + 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.map(&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.map(&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 6686832dc..2d548b9b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,7 @@ pub mod structs { DedupBy, Interleave, InterleaveShortest, + FilterMapResults, FilterResults, Product, PutBack, @@ -731,6 +732,24 @@ pub trait Itertools : Iterator { adaptors::filter_results(self, f) } + /// Return an iterator adaptor that filters and transforms every + /// `Result::Ok` value with the provided closure. `Result::Err` + /// values are unchanged. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let input = vec![Ok(22), Err(false), Ok(11)]; + /// let it = input.into_iter().filter_map_results(|i| if i > 20 { Some(i * 2) } else { None }); + /// itertools::assert_equal(it, vec![Ok(44), Err(false)]); + /// ``` + fn filter_map_results(self, f: F) -> FilterMapResults + where Self: Iterator> + Sized, + F: FnMut(T) -> Option, + { + adaptors::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.