1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Equi-joins.
//
// Spliced into the `FluentIter` trait by `build.rs`. Names resolve in
// the crate-root scope of `lib.rs`, exactly as before the split.
/// ⇔ `.combine(other, keySelf, keyOther)`: inner equi-join yielding
/// **all** matching `(left, right)` pairs in left order (right side is
/// indexed for `O(n + m)`; duplicate keys explode into pairs exactly
/// like the JS side).
///
/// JS shares object references between pairs; Rust must clone to hand
/// owned pairs out. Joining iterators *of references*
/// (`a.iter().combine(b.iter(), …)`) keeps the clone pointer-cheap.
///
/// ```
/// use fluent_iterable::FluentIter;
///
/// let left = [1, 2, 1];
/// let right = [(1, "x"), (1, "y"), (3, "z")];
/// let joined: Vec<(&i32, &(i32, &str))> =
/// left.iter().combine(right.iter(), |l| **l, |(k, _)| *k).collect();
/// let shape: Vec<(i32, &str)> =
/// joined.into_iter().map(|(l, (_, n))| (*l, *n)).collect();
/// assert_eq!(shape, vec![(1, "x"), (1, "y"), (1, "x"), (1, "y")]);
/// ```
///
/// Lazy: the right side is indexed on the first pull, the left side
/// streams — `take(k)` stops the left walk at `k` pairs.
/// ⇔ `.combineJoin(baseKey)` on an iterator **of iterables**: reduces
/// the whole family into one table of rows by a shared key — inner
/// join, pairwise folded (`n` iterables produce rows of length `n`).
/// The key is taken from the row's first element, exactly like the JS
/// `([a]) => key(a)`. Once a fold step matches nothing the result is
/// empty and stays empty.
///
/// ```
/// use fluent_iterable::FluentIter;
///
/// let a = [(1, "a1"), (2, "a2")];
/// let b = [(1, "b1"), (2, "b2")];
/// let c = [(1, "c1")];
/// let rows = [a.as_slice(), b.as_slice(), c.as_slice()]
/// .into_iter()
/// .combine_join(|(k, _)| *k);
/// assert_eq!(rows.len(), 1);
/// assert_eq!(
/// rows[0].iter().map(|(_, v)| *v).collect::<Vec<_>>(),
/// vec!["a1", "b1", "c1"]
/// );
/// ```
///
/// Resolving, like [`group_by_stable`][FluentIter::group_by_stable]:
/// the fold reads every iterable before any row is final, so the row
/// table comes back as a `Vec` instead of a buffering wrapper.