I'm looking for an iterator adapter that combines (multiplexes) the elements of two input iterators by conditionally replacing elements from the primary input iterator with those from the secondary input iterator, based on some predicate function.
For illustration I'm looking for an iterator kinda like this
struct MuxIter<A, B, F> {
primary: A, // primary input iterator
secondary: B, // secondary input iterator
predicate: F, // predicate that determines which input iterator to yield values from
}
impl<A, B, F, T> Iterator for MuxIter<A, B, F>
where
A: Iterator<Item = T>,
B: Iterator<Item = T>,
F: Fn(&T) -> bool,
{
type Item = T;
#[rustfmt::skip]
fn next(&mut self) -> Option<Self::Item> {
let elem = self.primary.next()?; // First get the next element from the main iterator
if (self.predicate)(&elem) { // Check if it passes the predicate...
Some(elem) // If it does, yeild it
} else {
self.secondary.next() // if it doesn't, yeild the next element from the secondary iterator instead
}
}
}
See playground for full illustration of this in action.
Does such an iterator exist in std
or in a 3rd party crate?
There is a facility in std
that can be used to implement this: filter_map
.
fn mux_iter<'a, I1, I2, T, F>(
primary: I1,
secondary: I2,
predicate: F,
) -> impl Iterator<Item = T> + 'a
where
I1: IntoIterator<Item = T>,
I1::IntoIter: 'a,
I2: IntoIterator<Item = T>,
I2::IntoIter: 'a,
F: Fn(&T) -> bool + 'a,
{
let mut secondary = secondary.into_iter();
primary.into_iter().filter_map(move |v| {
if predicate(&v) {
Some(v)
} else {
secondary.next()
}
})
}
Note that there is a bug in your implementation: if the predicate returns false
and the secondary iterator has no more items, your iterator will return None
instead of continuing to the next element from the primary iterator. The filter_map
-based iterator above does not have this bug.