use std::cmp::Ordering; use std::iter::Fuse; use std::fmt; use super::adaptors::{PutBack, put_back}; use crate::either_or_both::EitherOrBoth; #[cfg(doc)] use crate::Itertools; /// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order. /// /// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`]. pub fn merge_join_by(left: I, right: J, cmp_fn: F) -> MergeJoinBy where I: IntoIterator, J: IntoIterator, F: FnMut(&I::Item, &J::Item) -> Ordering { MergeJoinBy { left: put_back(left.into_iter().fuse()), right: put_back(right.into_iter().fuse()), cmp_fn, } } /// An iterator adaptor that merge-joins items from the two base iterators in ascending order. /// /// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MergeJoinBy { left: PutBack>, right: PutBack>, cmp_fn: F } impl Clone for MergeJoinBy where I: Iterator, J: Iterator, PutBack>: Clone, PutBack>: Clone, F: Clone, { clone_fields!(left, right, cmp_fn); } impl fmt::Debug for MergeJoinBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, J: Iterator + fmt::Debug, J::Item: fmt::Debug, { debug_fmt_fields!(MergeJoinBy, left, right); } impl Iterator for MergeJoinBy where I: Iterator, J: Iterator, F: FnMut(&I::Item, &J::Item) -> Ordering { type Item = EitherOrBoth; fn next(&mut self) -> Option { match (self.left.next(), self.right.next()) { (None, None) => None, (Some(left), None) => Some(EitherOrBoth::Left(left)), (None, Some(right)) => Some(EitherOrBoth::Right(right)), (Some(left), Some(right)) => { match (self.cmp_fn)(&left, &right) { Ordering::Equal => Some(EitherOrBoth::Both(left, right)), Ordering::Less => { self.right.put_back(right); Some(EitherOrBoth::Left(left)) }, Ordering::Greater => { self.left.put_back(left); Some(EitherOrBoth::Right(right)) } } } } } fn size_hint(&self) -> (usize, Option) { let (a_lower, a_upper) = self.left.size_hint(); let (b_lower, b_upper) = self.right.size_hint(); let lower = ::std::cmp::max(a_lower, b_lower); let upper = match (a_upper, b_upper) { (Some(x), Some(y)) => x.checked_add(y), _ => None, }; (lower, upper) } fn count(mut self) -> usize { let mut count = 0; loop { match (self.left.next(), self.right.next()) { (None, None) => break count, (Some(_left), None) => break count + 1 + self.left.into_parts().1.count(), (None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(), (Some(left), Some(right)) => { count += 1; match (self.cmp_fn)(&left, &right) { Ordering::Equal => {} Ordering::Less => self.right.put_back(right), Ordering::Greater => self.left.put_back(left), } } } } } fn last(mut self) -> Option { let mut previous_element = None; loop { match (self.left.next(), self.right.next()) { (None, None) => break previous_element, (Some(left), None) => { break Some(EitherOrBoth::Left( self.left.into_parts().1.last().unwrap_or(left), )) } (None, Some(right)) => { break Some(EitherOrBoth::Right( self.right.into_parts().1.last().unwrap_or(right), )) } (Some(left), Some(right)) => { previous_element = match (self.cmp_fn)(&left, &right) { Ordering::Equal => Some(EitherOrBoth::Both(left, right)), Ordering::Less => { self.right.put_back(right); Some(EitherOrBoth::Left(left)) } Ordering::Greater => { self.left.put_back(left); Some(EitherOrBoth::Right(right)) } } } } } } fn nth(&mut self, mut n: usize) -> Option { loop { if n == 0 { break self.next(); } n -= 1; match (self.left.next(), self.right.next()) { (None, None) => break None, (Some(_left), None) => break self.left.nth(n).map(EitherOrBoth::Left), (None, Some(_right)) => break self.right.nth(n).map(EitherOrBoth::Right), (Some(left), Some(right)) => match (self.cmp_fn)(&left, &right) { Ordering::Equal => {} Ordering::Less => self.right.put_back(right), Ordering::Greater => self.left.put_back(left), }, } } } }