1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/// Find all duplicate elements in a collection based on a key generated by the iteratee function.
/// Each duplicate element (beyond the first occurrence) appears in the result.
///
/// # Arguments
/// * `collection` - A slice of items.
/// * `iteratee` - A function that takes an item and returns a key used for duplicate detection.
///
/// # Returns
/// * `Vec<T>` - A vector containing all duplicate elements beyond their first occurrence.
///
/// # Examples
/// ```rust
/// use lowdash::find_duplicates_by;
/// let numbers = vec![1, 2, 3, 4];
/// let result = find_duplicates_by(&numbers, |x| x % 2);
/// assert_eq!(result, vec![3, 4]); // Second occurrences of duplicated keys
/// ```
///
/// ```rust
/// use lowdash::find_duplicates_by;
///
/// #[derive(Debug, Clone, PartialEq)]
/// struct Person {
/// name: String,
/// age: u32,
/// }
///
/// let people = vec![
/// Person { name: "Alice".to_string(), age: 25 },
/// Person { name: "Bob".to_string(), age: 30 },
/// Person { name: "Carol".to_string(), age: 25 },
/// ];
///
/// let result = find_duplicates_by(&people, |p| p.age);
/// assert_eq!(result, vec![
/// Person { name: "Carol".to_string(), age: 25 },
/// ]);
/// ```
pub fn find_duplicates_by<T, U, F>(collection: &[T], iteratee: F) -> Vec<T>
where
T: Clone,
U: Eq + std::hash::Hash + Clone,
F: Fn(&T) -> U,
{
use std::collections::HashMap;
// First pass: count occurrences of each key
let mut key_counts: HashMap<U, usize> = HashMap::new();
for item in collection {
let key = iteratee(item);
*key_counts.entry(key).or_insert(0) += 1;
}
let mut seen: HashMap<U, usize> = HashMap::new();
let mut result = Vec::new();
// Second pass: collect duplicates beyond the first occurrence
for item in collection {
let key = iteratee(item);
if let Some(&count) = key_counts.get(&key) {
if count > 1 {
let entry = seen.entry(key.clone()).or_insert(0);
if *entry >= 1 {
result.push(item.clone());
}
*entry += 1;
}
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_duplicates_by_remainder() {
let numbers = vec![1, 2, 3, 4];
let result = find_duplicates_by(&numbers, |x| x % 2);
assert_eq!(result, vec![3, 4]); // Second occurrences of duplicated keys
}
#[test]
fn test_find_duplicates_by_length() {
let words = vec!["a", "ab", "cd", "ef", "abcd"];
let result = find_duplicates_by(&words, |s| s.len());
assert_eq!(result, vec!["cd", "ef"]); // Words with length 2 appear more than once
}
#[test]
fn test_find_duplicates_by_empty() {
let collection: Vec<i32> = vec![];
let result = find_duplicates_by(&collection, |x| x % 2);
assert_eq!(result, Vec::<i32>::new());
}
#[test]
fn test_find_duplicates_by_with_struct() {
#[derive(Debug, Clone, PartialEq)]
struct Person {
name: String,
age: u32,
}
let people = vec![
Person {
name: "Alice".to_string(),
age: 25,
},
Person {
name: "Bob".to_string(),
age: 30,
},
Person {
name: "Carol".to_string(),
age: 25,
},
];
// Test duplicates by age
let result = find_duplicates_by(&people, |p| p.age);
assert_eq!(
result,
vec![Person {
name: "Carol".to_string(),
age: 25,
},]
);
// Test duplicates by name first letter
let name_result = find_duplicates_by(&people, |p| p.name.chars().next().unwrap());
assert_eq!(name_result, Vec::new()); // No duplicate first letters
}
#[test]
fn test_find_duplicates_by_all_same() {
let numbers = vec![1, 2, 3, 4];
let result = find_duplicates_by(&numbers, |_| 1);
assert_eq!(result, vec![2, 3, 4]); // All except first are duplicates
}
#[test]
fn test_find_duplicates_by_floats() {
let floats = vec![1.1, 2.2, 3.3, 2.2, 4.4, 1.1];
let result = find_duplicates_by(&floats, |x: &f64| (*x).to_bits());
assert_eq!(result, vec![2.2, 1.1]);
}
}