pub trait FluentIter: Iterator + Sized {
Show 27 methods
// Provided methods
fn group_by_stable<K, F>(self, key: F) -> Vec<(K, Vec<Self::Item>)>
where K: Eq + Hash + Clone,
F: FnMut(&Self::Item) -> K { ... }
fn combine<J, K, KL, KR>(
self,
other: J,
key_left: KL,
key_right: KR,
) -> Combine<Self, J::IntoIter, K, KL, KR> ⓘ
where J: IntoIterator,
K: Eq + Hash + Clone,
KL: FnMut(&Self::Item) -> K,
KR: FnMut(&J::Item) -> K,
Self::Item: Clone,
J::Item: Clone { ... }
fn combine_join<T, K, F>(self, key: F) -> Vec<Vec<T>>
where Self::Item: IntoIterator<Item = T>,
K: Eq + Hash,
T: Clone,
F: FnMut(&T) -> K { ... }
fn distinct(self) -> Distinct<Self> ⓘ
where Self::Item: Eq + Hash + Clone { ... }
fn distinct_by<K, F>(self, key: F) -> DistinctBy<Self, K, F> ⓘ
where K: Eq + Hash,
F: FnMut(&Self::Item) -> K { ... }
fn is_distinct(self) -> bool
where Self::Item: Eq + Hash { ... }
fn is_distinct_by<K>(self, key: impl FnMut(&Self::Item) -> K) -> bool
where K: Eq + Hash { ... }
fn when_empty<F>(self, fallback: F) -> WhenEmpty<Self, F> ⓘ
where F: IntoIterator<Item = Self::Item> { ... }
fn repeat(self, times: usize) -> Repeat<Self> ⓘ
where Self::Item: Clone { ... }
fn to_map_by<K, V>(
self,
key: impl FnMut(&Self::Item) -> K,
value: impl FnMut(&Self::Item) -> V,
) -> HashMap<K, V>
where K: Eq + Hash { ... }
fn to_map_chain<K1, K2>(
self,
key1: impl FnMut(&Self::Item) -> K1,
key2: impl FnMut(&Self::Item) -> K2,
) -> HashMap<K1, HashMap<K2, Vec<Self::Item>>>
where K1: Eq + Hash,
K2: Eq + Hash { ... }
fn to_map_chain_reduce<K1, K2, A>(
self,
key1: impl FnMut(&Self::Item) -> K1,
key2: impl FnMut(&Self::Item) -> K2,
init: A,
reduce: impl FnMut(A, Self::Item) -> A,
) -> HashMap<K1, HashMap<K2, A>>
where K1: Eq + Hash,
K2: Eq + Hash,
A: Clone { ... }
fn sorted(self) -> Vec<Self::Item>
where Self::Item: Ord { ... }
fn sort_with<F>(self, compare: F) -> Vec<Self::Item>
where F: FnMut(&Self::Item, &Self::Item) -> Ordering { ... }
fn sort_by<K, F>(self, key: F) -> Vec<Self::Item>
where K: Ord,
F: FnMut(&Self::Item) -> K { ... }
fn sort_by_desc<K, F>(self, key: F) -> Vec<Self::Item>
where K: Ord,
F: FnMut(&Self::Item) -> K { ... }
fn avg(self) -> Option<f64>
where Self::Item: Into<f64> { ... }
fn avg_by(self, value: impl FnMut(&Self::Item) -> f64) -> Option<f64> { ... }
fn join(self, separator: &str) -> String
where Self::Item: Display { ... }
fn join_by<S: Display>(
self,
value: impl FnMut(&Self::Item) -> S,
separator: &str,
) -> String { ... }
fn has_exactly(self, n: usize) -> bool { ... }
fn has_less_than(self, n: usize) -> bool { ... }
fn has_less_or_exactly(self, n: usize) -> bool { ... }
fn has_more_than(self, n: usize) -> bool { ... }
fn has_more_or_exactly(self, n: usize) -> bool { ... }
fn reduce_and_map<A, R>(
self,
init: A,
fold: impl FnMut(A, Self::Item) -> A,
map: impl FnMut(A) -> R,
) -> R { ... }
fn contains(self, item: &Self::Item) -> bool
where Self::Item: PartialEq { ... }
}Expand description
The missing combinators of std::iter::Iterator, in the
[@codibre/fluent-iterable] vocabulary (see the crate docs for
the full npm ⇄ Rust map).
Blanket-implemented for every iterator — just use fluent_iterable::FluentIter;.
§Laziness model
Streaming adapters are reserved for operations where laziness can pay:
distinct, distinct_by
and combine short-circuit under take(k) and
never buffer the sequence. Operations whose first result is not final
until the whole source has been read — group_by_stable,
combine_join and the sort family — are
inherently O(N) and resolve to a solid Vec right away: a lazy wrapper
over a full pass would consume the same source and store the same
results, while losing re-iterability and every Vec method. Chaining
still works (Vec is an IntoIterator); terminals (avg, join,
to_map*, has_*, …) return plain values like npm does.
Provided Methods§
Sourcefn group_by_stable<K, F>(self, key: F) -> Vec<(K, Vec<Self::Item>)>
fn group_by_stable<K, F>(self, key: F) -> Vec<(K, Vec<Self::Item>)>
⇔ .group(keyFn): stable grouping — first-seen key order is
preserved and each group keeps encounter order.
Note [Iterator::group_by] only groups consecutive elements,
which is a different operation (that one is slice::group_by
territory); this is the npm group, which is a full pass.
use fluent_iterable::FluentIter;
let grouped = ["b", "a", "b", "c", "a"].into_iter().group_by_stable(|x| *x);
let keys: Vec<_> = grouped.iter().map(|(k, _)| *k).collect();
assert_eq!(keys, vec!["b", "a", "c"]);
assert_eq!(grouped[1].1, vec!["a", "a"]);Resolving: grouping is O(N) by nature — an element can extend a
group that was opened first, so no group is final before the whole
source is read. The Vec costs the same memory as any buffering
adapter and stays re-iterable; chain it with .into_iter() when you
want adapters over the groups.
Sourcefn combine<J, K, KL, KR>(
self,
other: J,
key_left: KL,
key_right: KR,
) -> Combine<Self, J::IntoIter, K, KL, KR> ⓘ
fn combine<J, K, KL, KR>( self, other: J, key_left: KL, key_right: KR, ) -> Combine<Self, J::IntoIter, K, KL, KR> ⓘ
⇔ .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.
Sourcefn combine_join<T, K, F>(self, key: F) -> Vec<Vec<T>>
fn combine_join<T, K, F>(self, key: F) -> Vec<Vec<T>>
⇔ .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:
the fold reads every iterable before any row is final, so the row
table comes back as a Vec instead of a buffering wrapper.
Sourcefn distinct(self) -> Distinct<Self> ⓘ
fn distinct(self) -> Distinct<Self> ⓘ
⇔ .distinct() on hashable items: keeps the first occurrence of
every item. Lazy — buffers only the seen-set, and short-circuits
downstream (take(k) stops pulling once k uniques are out).
Item must be Clone (like itertools::unique): the set retains a
copy of each value to compare against while the occurrence itself is
yielded. Use FluentIter::distinct_by when cloning is not
available — it tracks borrowed keys instead.
use fluent_iterable::FluentIter;
let out: Vec<i32> = [1, 2, 1, 3, 2].into_iter().distinct().collect();
assert_eq!(out, vec![1, 2, 3]);Sourcefn distinct_by<K, F>(self, key: F) -> DistinctBy<Self, K, F> ⓘ
fn distinct_by<K, F>(self, key: F) -> DistinctBy<Self, K, F> ⓘ
⇔ .distinct(keyFn): keep the first item seen for each key.
Purely lazy — buffers only the seen-key set, never the sequence, so
take(k) stops pulling the source once k distinct keys are out.
use fluent_iterable::FluentIter;
let out: Vec<&str> = ["bb", "a1", "b1", "a2", "c"]
.into_iter()
.distinct_by(|s| s.chars().next().unwrap())
.collect();
assert_eq!(out, vec!["bb", "a1", "c"]);Sourcefn is_distinct(self) -> bool
fn is_distinct(self) -> bool
⇔ .isDistinct(): are all items unique? Short-circuits on the first
duplicate (the source is not drained).
use fluent_iterable::FluentIter;
assert!([1, 2, 3].into_iter().is_distinct());
assert!(![1, 2, 1].into_iter().is_distinct());Sourcefn is_distinct_by<K>(self, key: impl FnMut(&Self::Item) -> K) -> bool
fn is_distinct_by<K>(self, key: impl FnMut(&Self::Item) -> K) -> bool
⇔ .isDistinct(keyFn): is every key unique? Short-circuits on the
first repetition.
use fluent_iterable::FluentIter;
assert!([("a", 1), ("b", 2)].into_iter().is_distinct_by(|(k, _)| *k));
assert!(![("a", 1), ("b", 2), ("a", 3)].into_iter().is_distinct_by(|(k, _)| *k));Sourcefn when_empty<F>(self, fallback: F) -> WhenEmpty<Self, F> ⓘwhere
F: IntoIterator<Item = Self::Item>,
fn when_empty<F>(self, fallback: F) -> WhenEmpty<Self, F> ⓘwhere
F: IntoIterator<Item = Self::Item>,
⇔ .whenEmpty(fallback): yield from the source; if it produces
nothing, yield the fallback instead. A non-empty source never
touches the fallback iterable.
Unlike JS the fallback must yield the same Item type — map it
first if it doesn’t.
use fluent_iterable::FluentIter;
let empty: [i32; 0] = [];
let out: Vec<i32> = empty.into_iter().when_empty([7, 8]).collect();
assert_eq!(out, vec![7, 8]);
let out: Vec<i32> = [1].into_iter().when_empty([7, 8]).collect();
assert_eq!(out, vec![1]);Sourcefn repeat(self, times: usize) -> Repeat<Self> ⓘ
fn repeat(self, times: usize) -> Repeat<Self> ⓘ
⇔ .repeat(n): yield the whole sequence n times (first pass
streams and caches, later passes replay). n == 0 yields nothing
and never consumes the source. Unlike std’s nightly-only cycle,
this is finite.
use fluent_iterable::FluentIter;
let out: Vec<i32> = [1, 2].into_iter().repeat(3).collect();
assert_eq!(out, vec![1, 2, 1, 2, 1, 2]);
let out: Vec<i32> = [1, 2].into_iter().repeat(0).collect();
assert!(out.is_empty());Sourcefn to_map_by<K, V>(
self,
key: impl FnMut(&Self::Item) -> K,
value: impl FnMut(&Self::Item) -> V,
) -> HashMap<K, V>
fn to_map_by<K, V>( self, key: impl FnMut(&Self::Item) -> K, value: impl FnMut(&Self::Item) -> V, ) -> HashMap<K, V>
⇔ .toMap(keyFn, valueFn): later entries overwrite earlier ones for
the same key (JS Map.set semantics; HashMap iteration order is
unspecified, like the JS engine’s).
use fluent_iterable::FluentIter;
let items = vec![("k", 1), ("j", 2), ("k", 3)];
let map = items.into_iter().to_map_by(|(k, _)| *k, |(_, v)| *v);
assert_eq!(map.get("k"), Some(&3));Sourcefn to_map_chain<K1, K2>(
self,
key1: impl FnMut(&Self::Item) -> K1,
key2: impl FnMut(&Self::Item) -> K2,
) -> HashMap<K1, HashMap<K2, Vec<Self::Item>>>
fn to_map_chain<K1, K2>( self, key1: impl FnMut(&Self::Item) -> K1, key2: impl FnMut(&Self::Item) -> K2, ) -> HashMap<K1, HashMap<K2, Vec<Self::Item>>>
⇔ .toMapChain(k1, k2): two-level index — key1 outer, key2
inner, leaves keep encounter order.
JS chains to an arbitrary number of key functions; Rust fixes the nested type at depth 2 — deeper nesting is a fold of these layers.
use fluent_iterable::FluentIter;
let rows = [("sp", "sa", 1), ("sp", "rj", 2), ("mg", "sa", 3)];
let index = rows
.into_iter()
.to_map_chain(|(a, _, _)| *a, |(_, b, _)| *b);
assert_eq!(index["sp"]["sa"], vec![("sp", "sa", 1)]);
assert_eq!(index["sp"]["rj"], vec![("sp", "rj", 2)]);Sourcefn to_map_chain_reduce<K1, K2, A>(
self,
key1: impl FnMut(&Self::Item) -> K1,
key2: impl FnMut(&Self::Item) -> K2,
init: A,
reduce: impl FnMut(A, Self::Item) -> A,
) -> HashMap<K1, HashMap<K2, A>>
fn to_map_chain_reduce<K1, K2, A>( self, key1: impl FnMut(&Self::Item) -> K1, key2: impl FnMut(&Self::Item) -> K2, init: A, reduce: impl FnMut(A, Self::Item) -> A, ) -> HashMap<K1, HashMap<K2, A>>
⇔ .toMapChainReduce(k1, k2, reducer): two-level index whose leaves
are folded instead of collected. JS folds without an explicit
initial value; Rust demands one (there is no undefined).
use fluent_iterable::FluentIter;
let rows = [("sp", "sa", 1), ("sp", "sa", 2), ("sp", "rj", 5)];
let totals = rows.into_iter().to_map_chain_reduce(
|(a, _, _)| *a,
|(_, b, _)| *b,
0,
|acc, (_, _, v)| acc + v,
);
assert_eq!(totals["sp"]["sa"], 3);
assert_eq!(totals["sp"]["rj"], 5);Sourcefn sorted(self) -> Vec<Self::Item>
fn sorted(self) -> Vec<Self::Item>
⇔ .sort() with the type’s own ordering: collect and stable
sort. (JS would stringify non-comparable items; Rust uses Ord.)
use fluent_iterable::FluentIter;
assert_eq!([3, 1, 2].into_iter().sorted(), vec![1, 2, 3]);Resolving: nothing beats the minimum until the source is drained, so
a lazy sort wrapper can never short-circuit — the sorted Vec does
the same work once and adds re-iterability plus every Vec method
(npm’s IterableSorted buffering made sense in JS, not here).
Sourcefn sort_with<F>(self, compare: F) -> Vec<Self::Item>
fn sort_with<F>(self, compare: F) -> Vec<Self::Item>
⇔ .sort(compareFn): stable sort with a custom comparator.
use fluent_iterable::FluentIter;
assert_eq!([1, 2, 3].into_iter().sort_with(|a, b| b.cmp(a)), vec![3, 2, 1]);Resolving, like FluentIter::sorted.
Sourcefn sort_by<K, F>(self, key: F) -> Vec<Self::Item>
fn sort_by<K, F>(self, key: F) -> Vec<Self::Item>
⇔ .sortBy(keyFn): stable sort ascending by key. Ties keep encounter
order. As with slice::sort_by_key the key function may be called
more than once per element — keep it cheap, or sort decorated values.
use fluent_iterable::FluentIter;
let rows = [("b", 1), ("a", 2), ("b", 3)];
let sorted = rows.into_iter().sort_by(|(_, v)| *v);
assert_eq!(sorted, vec![("b", 1), ("a", 2), ("b", 3)]);
assert_eq!(sorted.first().copied(), Some(("b", 1)));Resolving, like FluentIter::sorted — but only pay for what you
need: if the goal is the least rather than the whole order, use
Iterator::min_by_key (or Iterator::max_by_key) directly.
One pass, no allocation, and std documents first-wins on ties —
the exact semantics of sortBy(k).first() over .sort(k => k) in
JS, without the O(n log n) sort and the intermediate Vec.
use fluent_iterable::FluentIter; // for the sibling vocabulary
let rows = [("b", 1), ("a", 2), ("b", 3)];
// sortBy(v => v).first() ⇔ min_by_key, in O(n):
assert_eq!(rows.iter().min_by_key(|(_, v)| *v).copied(), Some(("b", 1)));Sourcefn sort_by_desc<K, F>(self, key: F) -> Vec<Self::Item>
fn sort_by_desc<K, F>(self, key: F) -> Vec<Self::Item>
⇔ .sortBy('-keyFn') (the descending order descriptor): stable sort
descending by key; ties keep encounter order.
use fluent_iterable::FluentIter;
let rows = [("b", 1), ("a", 2), ("b", 3)];
assert_eq!(
rows.into_iter().sort_by_desc(|(_, v)| *v),
vec![("b", 3), ("a", 2), ("b", 1)]
);Resolving, like FluentIter::sorted.
Sourcefn avg(self) -> Option<f64>
fn avg(self) -> Option<f64>
⇔ .avg(): arithmetic mean as Option<f64> — None on empty
(JS returns NaN; Rust says it in the type).
use fluent_iterable::FluentIter;
assert_eq!([2, 4, 6].into_iter().avg(), Some(4.0));
assert_eq!(std::iter::empty::<i32>().avg(), None);Sourcefn avg_by(self, value: impl FnMut(&Self::Item) -> f64) -> Option<f64>
fn avg_by(self, value: impl FnMut(&Self::Item) -> f64) -> Option<f64>
⇔ .avg(mapFn): mean of a projection.
use fluent_iterable::FluentIter;
let rows = [("x", 10.0), ("y", 20.0)];
assert_eq!(rows.into_iter().avg_by(|(_, v)| *v), Some(15.0));Sourcefn join(self, separator: &str) -> String
fn join(self, separator: &str) -> String
⇔ .join(sep): display every item with the separator between.
(JS skips nullish items; Rust has neither, every item is shown.)
use fluent_iterable::FluentIter;
assert_eq!([1, 2, 3].into_iter().join(", "), "1, 2, 3");Sourcefn join_by<S: Display>(
self,
value: impl FnMut(&Self::Item) -> S,
separator: &str,
) -> String
fn join_by<S: Display>( self, value: impl FnMut(&Self::Item) -> S, separator: &str, ) -> String
⇔ .join(sep, mapFn): project each item before joining.
use fluent_iterable::FluentIter;
let out = [1, 2].into_iter().join_by(|v| format!("{v:>3}"), "-");
assert_eq!(out, " 1- 2");Sourcefn has_exactly(self, n: usize) -> bool
fn has_exactly(self, n: usize) -> bool
⇔ .hasExactly(n): exactly n items — and no more. Short-circuits
after n + 1 items.
use fluent_iterable::FluentIter;
assert!([1, 2].into_iter().has_exactly(2));
assert!(![1, 2, 3].into_iter().has_exactly(2));Sourcefn has_less_than(self, n: usize) -> bool
fn has_less_than(self, n: usize) -> bool
⇔ .hasLessThan(n): strictly fewer than n items. Short-circuits
after n + 1.
use fluent_iterable::FluentIter;
assert!([1].into_iter().has_less_than(2));
assert!(![1, 2].into_iter().has_less_than(2));Sourcefn has_less_or_exactly(self, n: usize) -> bool
fn has_less_or_exactly(self, n: usize) -> bool
⇔ .hasLessOrExactly(n): at most n items. Short-circuits after
n + 1.
use fluent_iterable::FluentIter;
assert!([1, 2].into_iter().has_less_or_exactly(2));
assert!(![1, 2, 3].into_iter().has_less_or_exactly(2));Sourcefn has_more_than(self, n: usize) -> bool
fn has_more_than(self, n: usize) -> bool
⇔ .hasMoreThan(n): strictly more than n items. Short-circuits
after n + 1.
use fluent_iterable::FluentIter;
assert!([1, 2, 3].into_iter().has_more_than(2));
assert!(![1, 2].into_iter().has_more_than(2));Sourcefn has_more_or_exactly(self, n: usize) -> bool
fn has_more_or_exactly(self, n: usize) -> bool
⇔ .hasMoreOrExactly(n): at least n items. Short-circuits after
n — it cannot tell you how many more.
use fluent_iterable::FluentIter;
assert!([1, 2].into_iter().has_more_or_exactly(2));
assert!(![1].into_iter().has_more_or_exactly(2));Sourcefn reduce_and_map<A, R>(
self,
init: A,
fold: impl FnMut(A, Self::Item) -> A,
map: impl FnMut(A) -> R,
) -> R
fn reduce_and_map<A, R>( self, init: A, fold: impl FnMut(A, Self::Item) -> A, map: impl FnMut(A) -> R, ) -> R
⇔ .reduceAndMap(reducer, initial, resultFn): fold, then project the
accumulator. Argument order follows Iterator::fold (initial
first), not the JS signature.
use fluent_iterable::FluentIter;
let report = [1, 2, 3]
.into_iter()
.reduce_and_map(0, |acc, x| acc + x, |sum| format!("total {sum}"));
assert_eq!(report, "total 6");Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".