rollercoaster/
unique.rs

1use std::{collections::HashSet, hash::Hash};
2
3pub struct Unique<I, K, F> {
4    seen: HashSet<K>,
5    underlying: I,
6    identify: F,
7}
8
9impl<I, K, F> Unique<I, K, F>
10where
11    I: Iterator,
12    K: Hash,
13    F: FnMut(&I::Item) -> K,
14{
15    pub(crate) fn new(iter: I, identify: F) -> Self {
16        Self {
17            seen: Default::default(),
18            underlying: iter,
19            identify,
20        }
21    }
22}
23
24impl<I, K, F> Iterator for Unique<I, K, F>
25where
26    I: Iterator,
27    K: Eq + Hash,
28    F: FnMut(&I::Item) -> K,
29{
30    type Item = I::Item;
31
32    fn next(&mut self) -> Option<Self::Item> {
33        let item = self.underlying.find(|i| {
34            let key = (self.identify)(i);
35            !self.seen.contains(&key)
36        });
37
38        if let Some(i) = item.as_ref() {
39            let key = (self.identify)(i);
40            self.seen.insert(key);
41        }
42
43        item
44    }
45}
46
47ext_impl! {
48    /// Creates an iterator that returns only unique values.
49    ///
50    /// The closure `F` is called on each item, where the returned value `K`
51    /// is checked against a [HashSet](std::collections::HashSet).
52    /// This means that `K` must implement
53    /// [Hash] + [Eq].
54    ///
55    /// If you are working with simple values, you can try
56    /// using [`unique`](crate::Rollercoaster::unique) instead.
57    ///
58    /// # Example
59    /// ```
60    /// # use rollercoaster::Rollercoaster;
61    /// let words = vec![
62    ///     "I",
63    ///     "A",
64    ///     "am",
65    ///     "no",
66    ///     "to",
67    ///     "ha",
68    ///     "unique",
69    ///     "people",
70    ///     "me",
71    /// ];
72    ///
73    /// let result: Vec<_> = words
74    ///     .into_iter()
75    ///     .unique_by(|s| s.len())
76    ///     .collect();
77    ///
78    /// assert_eq!(result, vec!["I", "am", "unique"]);
79    /// ```
80    fn unique_by<K, F>(self, identify: F) -> Unique<Self, K, F>
81    where
82        K: Hash + Eq,
83        F: FnMut(&Self::Item) -> K
84    {
85        Unique::new(self, identify)
86    }
87
88
89    /// Creates an iterator that returns unique values.
90    ///
91    /// Each value is cloned and checked against a [HashSet](std::collections::HashSet).
92    /// This means [`Item`](Iterator::Item) must implement
93    /// [Eq] + [Hash] + [Copy].
94    ///
95    /// If you need to specify a custom key per item,
96    /// use [`unique_by()`](crate::Rollercoaster::unique_by) instead.
97    ///
98    /// # Example
99    /// ```
100    /// # use rollercoaster::Rollercoaster;
101    /// let result: Vec<_> = vec![1, 1, 4, 5, 2, 1, 4, 3, 2]
102    ///     .into_iter()
103    ///     .unique()
104    ///     .collect();
105    ///
106    /// assert_eq!(result, vec![1, 4, 5, 2, 3]);
107    /// ```
108    fn unique(self) -> Unique<Self, Self::Item, fn(&Self::Item) -> Self::Item>
109    where
110        Self::Item: Hash + Eq + Copy
111    {
112        Unique::new(self, |i| *i)
113    }
114}
115
116#[cfg(test)]
117mod test {
118    use crate::Rollercoaster;
119
120    #[test]
121    fn it_returns_unique() {
122        let result: Vec<_> = vec![5, 2, 6, 7, 3, 3, 2, 4, 5]
123            .into_iter()
124            .unique()
125            .collect();
126
127        let result_two: Vec<_> = vec!["apple", "apple", "orange"]
128            .into_iter()
129            .unique_by(|s| s.len())
130            .collect();
131
132        assert_eq!(result, vec![5, 2, 6, 7, 3, 4]);
133        assert_eq!(result_two, vec!["apple", "orange"]);
134    }
135}