Skip to main content

fluent_iterable/ops/
distinct.rs

1//! Adapters for the distinct operations.
2
3use std::collections::HashSet;
4use std::hash::Hash;
5
6/// Streaming deduplication adapter behind [`FluentIter::distinct`].
7///
8/// Keeps the first occurrence of every item and drops later ones, holding
9/// one [`HashSet`] of what has been seen — it never buffers the sequence.
10#[derive(Debug)]
11pub struct Distinct<I: Iterator>
12where
13    I::Item: Clone,
14{
15    pub(crate) iter: I,
16    pub(crate) seen: HashSet<I::Item>,
17}
18
19impl<I> Iterator for Distinct<I>
20where
21    I: Iterator,
22    I::Item: Eq + Hash + Clone,
23{
24    type Item = I::Item;
25
26    fn next(&mut self) -> Option<I::Item> {
27        loop {
28            let item = self.iter.next()?;
29            // `seen` must retain the value to compare future items, while
30            // we hand this occurrence out — so the item clones itself into
31            // the set (same contract as itertools' `unique`).
32            if self.seen.insert(item.clone()) {
33                return Some(item);
34            }
35        }
36    }
37
38    fn size_hint(&self) -> (usize, Option<usize>) {
39        let (_, upper) = self.iter.size_hint();
40        (0, upper)
41    }
42}
43/// Keyed streaming dedup produced by [`FluentIter::distinct_by`] — see there.
44///
45/// Purely lazy: buffers only the seen-key [`HashSet`], never the sequence.
46/// `take(k)` stops pulling the source as soon as `k` distinct keys are out.
47#[derive(Debug)]
48pub struct DistinctBy<I, K, F>
49where
50    I: Iterator,
51{
52    pub(crate) iter: I,
53    pub(crate) key: F,
54    pub(crate) seen: HashSet<K>,
55}
56
57impl<I, K, F> Iterator for DistinctBy<I, K, F>
58where
59    I: Iterator,
60    K: Eq + Hash,
61    F: FnMut(&I::Item) -> K,
62{
63    type Item = I::Item;
64
65    fn next(&mut self) -> Option<I::Item> {
66        // Disjoint-field borrows (edition 2021 capture): iter is advanced
67        // while the key/seen state is consulted.
68        self.iter
69            .by_ref()
70            .find(|item| self.seen.insert((self.key)(item)))
71    }
72
73    fn size_hint(&self) -> (usize, Option<usize>) {
74        (0, self.iter.size_hint().1)
75    }
76}