Skip to content

Commit 5ad6f70

Browse files
Functions passed to merge_join_by can return booleans
1 parent ad2e401 commit 5ad6f70

File tree

2 files changed

+124
-32
lines changed

2 files changed

+124
-32
lines changed

src/lib.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,10 @@ pub trait Itertools : Iterator {
10121012
/// Create an iterator that merges items from both this and the specified
10131013
/// iterator in ascending order.
10141014
///
1015-
/// It chooses whether to pair elements based on the `Ordering` returned by the
1015+
/// The function can either return an `Ordering` variant or a boolean.
1016+
///
1017+
/// In the first case,
1018+
/// it chooses whether to pair elements based on the `Ordering` returned by the
10161019
/// specified compare function. At any point, inspecting the tip of the
10171020
/// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
10181021
/// `J::Item` respectively, the resulting iterator will:
@@ -1036,10 +1039,34 @@ pub trait Itertools : Iterator {
10361039
/// vec![Both(0, 0), Left(2), Right(3), Left(4), Both(6, 6), Left(8), Right(9)]
10371040
/// );
10381041
/// ```
1042+
///
1043+
/// In the second case,
1044+
/// it chooses whether to pair elements based on the boolean returned by the
1045+
/// specified function. At any point, inspecting the tip of the
1046+
/// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
1047+
/// `J::Item` respectively, the resulting iterator will:
1048+
///
1049+
/// - Emit `Either::Left(i)` when `true`,
1050+
/// and remove `i` from its source iterator
1051+
/// - Emit `Either::Right(j)` when `false`,
1052+
/// and remove `j` from its source iterator
1053+
///
1054+
/// ```
1055+
/// use itertools::Itertools;
1056+
/// use itertools::Either::{Left, Right};
1057+
///
1058+
/// let multiples_of_2 = (0..10).step_by(2);
1059+
/// let multiples_of_3 = (0..10).step_by(3);
1060+
///
1061+
/// itertools::assert_equal(
1062+
/// multiples_of_2.merge_join_by(multiples_of_3, |i, j| i <= j),
1063+
/// vec![Left(0), Right(0), Left(2), Right(3), Left(4), Left(6), Right(6), Left(8), Right(9)]
1064+
/// );
1065+
/// ```
10391066
#[inline]
1040-
fn merge_join_by<J, F>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F>
1067+
fn merge_join_by<J, F, R>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F, R>
10411068
where J: IntoIterator,
1042-
F: FnMut(&Self::Item, &J::Item) -> std::cmp::Ordering,
1069+
F: FnMut(&Self::Item, &J::Item) -> R,
10431070
Self: Sized
10441071
{
10451072
merge_join_by(self, other, cmp_fn)

src/merge_join.rs

Lines changed: 94 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::cmp::Ordering;
22
use std::iter::Fuse;
33
use std::fmt;
44

5+
use either::Either;
6+
57
use super::adaptors::{PutBack, put_back};
68
use crate::either_or_both::EitherOrBoth;
79
#[cfg(doc)]
@@ -10,11 +12,11 @@ use crate::Itertools;
1012
/// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
1113
///
1214
/// [`IntoIterator`] enabled version of [`Itertools::merge_join_by`].
13-
pub fn merge_join_by<I, J, F>(left: I, right: J, cmp_fn: F)
14-
-> MergeJoinBy<I::IntoIter, J::IntoIter, F>
15+
pub fn merge_join_by<I, J, F, R>(left: I, right: J, cmp_fn: F)
16+
-> MergeJoinBy<I::IntoIter, J::IntoIter, F, R>
1517
where I: IntoIterator,
1618
J: IntoIterator,
17-
F: FnMut(&I::Item, &J::Item) -> Ordering
19+
F: FnMut(&I::Item, &J::Item) -> R,
1820
{
1921
MergeJoinBy {
2022
left: put_back(left.into_iter().fuse()),
@@ -27,56 +29,119 @@ pub fn merge_join_by<I, J, F>(left: I, right: J, cmp_fn: F)
2729
///
2830
/// See [`.merge_join_by()`](crate::Itertools::merge_join_by) for more information.
2931
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
30-
pub struct MergeJoinBy<I: Iterator, J: Iterator, F> {
32+
pub struct MergeJoinBy<I, J, F, R>
33+
where I: Iterator,
34+
J: Iterator,
35+
F: FnMut(&I::Item, &J::Item) -> R,
36+
{
3137
left: PutBack<Fuse<I>>,
3238
right: PutBack<Fuse<J>>,
3339
cmp_fn: F
3440
}
3541

36-
impl<I, J, F> Clone for MergeJoinBy<I, J, F>
42+
pub trait OrderingOrBool<I, J> {
43+
type Item;
44+
fn into_cmp(self) -> Ordering;
45+
fn left(left: I) -> Self::Item;
46+
fn right(right: J) -> Self::Item;
47+
fn both(left: I, right: J) -> Self::Item;
48+
}
49+
50+
impl<I, J, F, R> Clone for MergeJoinBy<I, J, F, R>
3751
where I: Iterator,
3852
J: Iterator,
3953
PutBack<Fuse<I>>: Clone,
4054
PutBack<Fuse<J>>: Clone,
41-
F: Clone,
55+
F: FnMut(&I::Item, &J::Item) -> R + Clone,
4256
{
4357
clone_fields!(left, right, cmp_fn);
4458
}
4559

46-
impl<I, J, F> fmt::Debug for MergeJoinBy<I, J, F>
60+
impl<I, J, F, R> fmt::Debug for MergeJoinBy<I, J, F, R>
4761
where I: Iterator + fmt::Debug,
4862
I::Item: fmt::Debug,
4963
J: Iterator + fmt::Debug,
5064
J::Item: fmt::Debug,
65+
F: FnMut(&I::Item, &J::Item) -> R,
5166
{
5267
debug_fmt_fields!(MergeJoinBy, left, right);
5368
}
5469

55-
impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
70+
impl<I, J> OrderingOrBool<I, J> for Ordering {
71+
type Item = EitherOrBoth<I, J>;
72+
73+
#[inline(always)]
74+
fn into_cmp(self) -> Ordering {
75+
self
76+
}
77+
78+
#[inline(always)]
79+
fn left(left: I) -> Self::Item {
80+
EitherOrBoth::Left(left)
81+
}
82+
83+
#[inline(always)]
84+
fn right(right: J) -> Self::Item {
85+
EitherOrBoth::Right(right)
86+
}
87+
88+
#[inline(always)]
89+
fn both(left: I, right: J) -> Self::Item {
90+
EitherOrBoth::Both(left, right)
91+
}
92+
}
93+
94+
impl<I, J> OrderingOrBool<I, J> for bool {
95+
type Item = Either<I, J>;
96+
97+
#[inline(always)]
98+
fn into_cmp(self) -> Ordering {
99+
if self {
100+
Ordering::Less
101+
} else {
102+
Ordering::Greater
103+
}
104+
}
105+
106+
#[inline(always)]
107+
fn left(left: I) -> Self::Item {
108+
Either::Left(left)
109+
}
110+
111+
#[inline(always)]
112+
fn right(right: J) -> Self::Item {
113+
Either::Right(right)
114+
}
115+
116+
#[inline(always)]
117+
fn both(_left: I, _right: J) -> Self::Item {
118+
unreachable!("into_cmp never returns Ordering::Equal so this should never be called")
119+
}
120+
}
121+
122+
impl<I, J, F, R> Iterator for MergeJoinBy<I, J, F, R>
56123
where I: Iterator,
57124
J: Iterator,
58-
F: FnMut(&I::Item, &J::Item) -> Ordering
125+
F: FnMut(&I::Item, &J::Item) -> R,
126+
R: OrderingOrBool<I::Item, J::Item>,
59127
{
60-
type Item = EitherOrBoth<I::Item, J::Item>;
128+
type Item = R::Item;
61129

62130
fn next(&mut self) -> Option<Self::Item> {
63131
match (self.left.next(), self.right.next()) {
64132
(None, None) => None,
65-
(Some(left), None) =>
66-
Some(EitherOrBoth::Left(left)),
67-
(None, Some(right)) =>
68-
Some(EitherOrBoth::Right(right)),
133+
(Some(left), None) => Some(R::left(left)),
134+
(None, Some(right)) => Some(R::right(right)),
69135
(Some(left), Some(right)) => {
70-
match (self.cmp_fn)(&left, &right) {
71-
Ordering::Equal =>
72-
Some(EitherOrBoth::Both(left, right)),
136+
match (self.cmp_fn)(&left, &right).into_cmp() {
137+
Ordering::Equal => Some(R::both(left, right)),
73138
Ordering::Less => {
74139
self.right.put_back(right);
75-
Some(EitherOrBoth::Left(left))
140+
Some(R::left(left))
76141
},
77142
Ordering::Greater => {
78143
self.left.put_back(left);
79-
Some(EitherOrBoth::Right(right))
144+
Some(R::right(right))
80145
}
81146
}
82147
}
@@ -106,7 +171,7 @@ impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
106171
(None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(),
107172
(Some(left), Some(right)) => {
108173
count += 1;
109-
match (self.cmp_fn)(&left, &right) {
174+
match (self.cmp_fn)(&left, &right).into_cmp() {
110175
Ordering::Equal => {}
111176
Ordering::Less => self.right.put_back(right),
112177
Ordering::Greater => self.left.put_back(left),
@@ -122,25 +187,25 @@ impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
122187
match (self.left.next(), self.right.next()) {
123188
(None, None) => break previous_element,
124189
(Some(left), None) => {
125-
break Some(EitherOrBoth::Left(
190+
break Some(R::left(
126191
self.left.into_parts().1.last().unwrap_or(left),
127192
))
128193
}
129194
(None, Some(right)) => {
130-
break Some(EitherOrBoth::Right(
195+
break Some(R::right(
131196
self.right.into_parts().1.last().unwrap_or(right),
132197
))
133198
}
134199
(Some(left), Some(right)) => {
135-
previous_element = match (self.cmp_fn)(&left, &right) {
136-
Ordering::Equal => Some(EitherOrBoth::Both(left, right)),
200+
previous_element = match (self.cmp_fn)(&left, &right).into_cmp() {
201+
Ordering::Equal => Some(R::both(left, right)),
137202
Ordering::Less => {
138203
self.right.put_back(right);
139-
Some(EitherOrBoth::Left(left))
204+
Some(R::left(left))
140205
}
141206
Ordering::Greater => {
142207
self.left.put_back(left);
143-
Some(EitherOrBoth::Right(right))
208+
Some(R::right(right))
144209
}
145210
}
146211
}
@@ -156,9 +221,9 @@ impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
156221
n -= 1;
157222
match (self.left.next(), self.right.next()) {
158223
(None, None) => break None,
159-
(Some(_left), None) => break self.left.nth(n).map(EitherOrBoth::Left),
160-
(None, Some(_right)) => break self.right.nth(n).map(EitherOrBoth::Right),
161-
(Some(left), Some(right)) => match (self.cmp_fn)(&left, &right) {
224+
(Some(_left), None) => break self.left.nth(n).map(R::left),
225+
(None, Some(_right)) => break self.right.nth(n).map(R::right),
226+
(Some(left), Some(right)) => match (self.cmp_fn)(&left, &right).into_cmp() {
162227
Ordering::Equal => {}
163228
Ordering::Less => self.right.put_back(right),
164229
Ordering::Greater => self.left.put_back(left),

0 commit comments

Comments
 (0)