lowdash/
find_duplicates.rs

1/// Find all duplicate elements in a collection (elements that appear more than once).
2/// Each duplicate element appears exactly once in the result.
3///
4/// # Arguments
5/// * `collection` - A slice of items.
6///
7/// # Returns
8/// * `Vec<T>` - A vector containing one instance of each duplicate element.
9///
10/// # Examples
11/// ```rust
12/// use lowdash::find_duplicates;
13/// let numbers = vec![1, 2, 2, 3, 3, 4];
14/// let result = find_duplicates(&numbers);
15/// assert_eq!(result, vec![2, 3]);
16/// ```
17///
18/// ```rust
19/// use lowdash::find_duplicates;
20/// let words = vec!["apple", "banana", "apple", "cherry", "banana"];
21/// let result = find_duplicates(&words);
22/// assert_eq!(result, vec!["apple", "banana"]);
23/// ```
24///
25/// ```rust
26/// use lowdash::find_duplicates;
27///
28/// #[derive(Debug, Clone, PartialEq, Eq, Hash)]
29/// struct Person {
30///     name: String,
31///     age: u32,
32/// }
33///
34/// let people = vec![
35///     Person { name: "Alice".to_string(), age: 25 },
36///     Person { name: "Bob".to_string(), age: 30 },
37///     Person { name: "Alice".to_string(), age: 25 },
38/// ];
39///
40/// let result = find_duplicates(&people);
41/// assert_eq!(result, vec![Person { name: "Alice".to_string(), age: 25 }]);
42/// ```
43pub fn find_duplicates<T>(collection: &[T]) -> Vec<T>
44where
45    T: Clone + Eq + std::hash::Hash,
46{
47    use std::collections::HashMap;
48
49    let mut seen: HashMap<&T, bool> = HashMap::new();
50    let mut result = Vec::new();
51
52    // Track items and their duplicate status
53    for item in collection {
54        match seen.get(item) {
55            Some(&already_added) => {
56                if !already_added {
57                    result.push(item.clone());
58                    seen.insert(item, true);
59                }
60            }
61            None => {
62                seen.insert(item, false);
63            }
64        }
65    }
66
67    result
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn test_find_duplicates_numbers() {
76        let collection = vec![1, 2, 2, 3, 3, 4];
77        let result = find_duplicates(&collection);
78        assert_eq!(result, vec![2, 3]);
79    }
80
81    #[test]
82    fn test_find_duplicates_strings() {
83        let collection = vec!["apple", "banana", "apple", "cherry", "banana"];
84        let result = find_duplicates(&collection);
85        assert_eq!(result, vec!["apple", "banana"]);
86    }
87
88    #[test]
89    fn test_find_duplicates_empty() {
90        let collection: Vec<i32> = vec![];
91        let result = find_duplicates(&collection);
92        assert_eq!(result, Vec::<i32>::new());
93    }
94
95    #[test]
96    fn test_find_duplicates_no_duplicates() {
97        let collection = vec![1, 2, 3, 4, 5];
98        let result = find_duplicates(&collection);
99        assert_eq!(result, Vec::<i32>::new());
100    }
101
102    #[test]
103    fn test_find_duplicates_all_duplicates() {
104        let collection = vec![1, 1, 2, 2, 3, 3];
105        let result = find_duplicates(&collection);
106        assert_eq!(result, vec![1, 2, 3]);
107    }
108
109    #[test]
110    fn test_find_duplicates_multiple_occurrences() {
111        let collection = vec![1, 1, 1, 2, 2, 2];
112        let result = find_duplicates(&collection);
113        assert_eq!(result, vec![1, 2]);
114    }
115
116    #[test]
117    fn test_find_duplicates_with_struct() {
118        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
119        struct Person {
120            name: String,
121            age: u32,
122        }
123
124        let people = vec![
125            Person {
126                name: "Alice".to_string(),
127                age: 25,
128            },
129            Person {
130                name: "Bob".to_string(),
131                age: 30,
132            },
133            Person {
134                name: "Alice".to_string(),
135                age: 25,
136            },
137            Person {
138                name: "Bob".to_string(),
139                age: 30,
140            },
141        ];
142
143        let result = find_duplicates(&people);
144        assert_eq!(
145            result,
146            vec![
147                Person {
148                    name: "Alice".to_string(),
149                    age: 25,
150                },
151                Person {
152                    name: "Bob".to_string(),
153                    age: 30,
154                },
155            ]
156        );
157    }
158}