Skip to main content

FluentIter

Trait FluentIter 

Source
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§

Source

fn group_by_stable<K, F>(self, key: F) -> Vec<(K, Vec<Self::Item>)>
where K: Eq + Hash + Clone, F: FnMut(&Self::Item) -> K,

⇔ .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.

Source

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,

⇔ .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.

Source

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,

⇔ .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.

Source

fn distinct(self) -> Distinct<Self> ⓘ
where Self::Item: Eq + Hash + Clone,

⇔ .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]);
Source

fn distinct_by<K, F>(self, key: F) -> DistinctBy<Self, K, F> ⓘ
where K: Eq + Hash, F: FnMut(&Self::Item) -> K,

⇔ .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"]);
Source

fn is_distinct(self) -> bool
where Self::Item: Eq + Hash,

⇔ .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());
Source

fn is_distinct_by<K>(self, key: impl FnMut(&Self::Item) -> K) -> bool
where K: Eq + Hash,

⇔ .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));
Source

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]);
Source

fn repeat(self, times: usize) -> Repeat<Self> ⓘ
where Self::Item: Clone,

⇔ .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());
Source

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,

⇔ .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));
Source

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,

⇔ .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)]);
Source

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,

⇔ .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);
Source

fn sorted(self) -> Vec<Self::Item>
where Self::Item: Ord,

⇔ .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).

Source

fn sort_with<F>(self, compare: F) -> Vec<Self::Item>
where F: FnMut(&Self::Item, &Self::Item) -> Ordering,

⇔ .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.

Source

fn sort_by<K, F>(self, key: F) -> Vec<Self::Item>
where K: Ord, F: FnMut(&Self::Item) -> K,

⇔ .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. “Take the least” spells as .sort_by(k).first().copied() — first is inherent to Vec/slices (std Iterator has no first), which the resolving design gets you for free.

Source

fn sort_by_desc<K, F>(self, key: F) -> Vec<Self::Item>
where K: Ord, F: FnMut(&Self::Item) -> K,

⇔ .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.

Source

fn avg(self) -> Option<f64>
where Self::Item: Into<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);
Source

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));
Source

fn join(self, separator: &str) -> String
where Self::Item: Display,

⇔ .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");
Source

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");
Source

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));
Source

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));
Source

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));
Source

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));
Source

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));
Source

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");
Source

fn contains(self, item: &Self::Item) -> bool
where Self::Item: PartialEq,

⇔ .contains(item): any item equal to item (JS compares with ===; Rust uses PartialEq). Short-circuits on the first hit.

use fluent_iterable::FluentIter;

assert!(["a", "b"].into_iter().contains(&"b"));
assert!(!["a", "b"].into_iter().contains(&"c"));

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§