lowdash/
count_values.rs

1use std::collections::HashMap;
2use std::hash::Hash;
3
4/// Counts the number of occurrences of each value in a collection.
5///
6/// This function iterates over a slice of items and returns a `HashMap` where each key is a unique
7/// item from the collection, and the corresponding value is the number of times that item appears.
8///
9/// **Time Complexity:** O(n), where n is the number of elements in the collection.
10///
11/// # Arguments
12///
13/// * `collection` - A slice of items to be counted.
14///
15/// # Type Parameters
16///
17/// * `T` - The type of elements in the input collection. Must implement `Hash`, `Eq`, and `Clone`.
18///
19/// # Returns
20///
21/// * `HashMap<T, usize>` - A map where keys are unique items from the collection and values are their counts.
22///
23/// # Examples
24///
25/// ```rust
26/// use lowdash::count_values;
27/// use std::collections::HashMap;
28///
29/// let numbers = vec![1, 2, 2, 3, 4, 3, 5];
30/// let result = count_values(&numbers);
31/// let mut expected = HashMap::new();
32/// expected.insert(1, 1);
33/// expected.insert(2, 2);
34/// expected.insert(3, 2);
35/// expected.insert(4, 1);
36/// expected.insert(5, 1);
37/// assert_eq!(result, expected);
38/// ```
39///
40/// ```rust
41/// use lowdash::count_values;
42/// use std::collections::HashMap;
43///
44/// #[derive(Debug, PartialEq, Eq, Hash, Clone)]
45/// struct Person {
46///     name: String,
47///     age: u32,
48/// }
49///
50/// let people = vec![
51///     Person { name: "Alice".to_string(), age: 25 },
52///     Person { name: "Bob".to_string(), age: 30 },
53///     Person { name: "Alice".to_string(), age: 25 },
54///     Person { name: "Carol".to_string(), age: 35 },
55/// ];
56///
57/// let result = count_values(&people);
58/// let mut expected = HashMap::new();
59/// expected.insert(
60///     Person { name: "Alice".to_string(), age: 25 },
61///     2
62/// );
63/// expected.insert(
64///     Person { name: "Bob".to_string(), age: 30 },
65///     1
66/// );
67/// expected.insert(
68///     Person { name: "Carol".to_string(), age: 35 },
69///     1
70/// );
71/// assert_eq!(result, expected);
72/// ```
73pub fn count_values<T>(collection: &[T]) -> HashMap<T, usize>
74where
75    T: Hash + Eq + Clone,
76{
77    let mut result = HashMap::new();
78    for item in collection {
79        *result.entry(item.clone()).or_insert(0) += 1;
80    }
81    result
82}
83
84#[cfg(test)]
85mod tests {
86    use crate::common;
87
88    use super::*;
89    use std::collections::HashMap;
90
91    #[derive(Debug, PartialEq, Eq, Hash, Clone)]
92    struct Person {
93        name: String,
94        age: u32,
95    }
96
97    #[test]
98    fn test_count_values_integers() {
99        let numbers = vec![1, 2, 2, 3, 4, 3, 5];
100        let result = count_values(&numbers);
101        let mut expected = HashMap::new();
102        expected.insert(1, 1);
103        expected.insert(2, 2);
104        expected.insert(3, 2);
105        expected.insert(4, 1);
106        expected.insert(5, 1);
107        assert_eq!(result, expected);
108    }
109
110    #[test]
111    fn test_count_values_strings() {
112        let strings = vec!["apple", "banana", "apple", "cherry", "banana"];
113        let result = count_values(&strings);
114        let mut expected = HashMap::new();
115        expected.insert("apple", 2);
116        expected.insert("banana", 2);
117        expected.insert("cherry", 1);
118        assert_eq!(result, expected);
119    }
120
121    #[test]
122    fn test_count_values_with_structs() {
123        let people = vec![
124            Person {
125                name: "Alice".to_string(),
126                age: 25,
127            },
128            Person {
129                name: "Bob".to_string(),
130                age: 30,
131            },
132            Person {
133                name: "Alice".to_string(),
134                age: 25,
135            },
136            Person {
137                name: "Carol".to_string(),
138                age: 35,
139            },
140        ];
141
142        let result = count_values(&people);
143        let mut expected = HashMap::new();
144        expected.insert(
145            Person {
146                name: "Alice".to_string(),
147                age: 25,
148            },
149            2,
150        );
151        expected.insert(
152            Person {
153                name: "Bob".to_string(),
154                age: 30,
155            },
156            1,
157        );
158        expected.insert(
159            Person {
160                name: "Carol".to_string(),
161                age: 35,
162            },
163            1,
164        );
165        assert_eq!(result, expected);
166    }
167
168    #[test]
169    fn test_count_values_empty_collection() {
170        let empty: Vec<i32> = vec![];
171        let result = count_values(&empty);
172        let expected: HashMap<i32, usize> = HashMap::new();
173        assert_eq!(result, expected);
174    }
175
176    #[test]
177    fn test_count_values_no_duplicates() {
178        let collection = vec![1, 2, 3, 4, 5];
179        let result = count_values(&collection);
180        let mut expected = HashMap::new();
181        expected.insert(1, 1);
182        expected.insert(2, 1);
183        expected.insert(3, 1);
184        expected.insert(4, 1);
185        expected.insert(5, 1);
186        assert_eq!(result, expected);
187    }
188
189    #[test]
190    fn test_count_values_all_duplicates() {
191        let collection = vec![2, 2, 2, 2];
192        let result = count_values(&collection);
193        let mut expected = HashMap::new();
194        expected.insert(2, 4);
195        assert_eq!(result, expected);
196    }
197
198    #[test]
199    fn test_count_values_with_optionals() {
200        let collection = vec![Some(1), None, Some(2), Some(1), None, Some(3), Some(2)];
201        let result = count_values(&collection);
202        let mut expected = HashMap::new();
203        expected.insert(Some(1), 2);
204        expected.insert(None, 2);
205        expected.insert(Some(2), 2);
206        expected.insert(Some(3), 1);
207        assert_eq!(result, expected);
208    }
209
210    #[test]
211    fn test_count_values_with_floats() {
212        let float_collection = vec![
213            common::Float(1.1),
214            common::Float(2.2),
215            common::Float(2.2),
216            common::Float(3.3),
217            common::Float(4.4),
218            common::Float(3.3),
219            common::Float(5.5),
220        ];
221        let result = count_values(&float_collection);
222        let mut expected = HashMap::new();
223        expected.insert(common::Float(1.1), 1);
224        expected.insert(common::Float(2.2), 2);
225        expected.insert(common::Float(3.3), 2);
226        expected.insert(common::Float(4.4), 1);
227        expected.insert(common::Float(5.5), 1);
228        assert_eq!(result, expected);
229    }
230
231    #[test]
232    fn test_count_values_with_characters() {
233        let chars = vec!['a', 'b', 'a', 'c', 'b', 'd'];
234        let result = count_values(&chars);
235        let mut expected = HashMap::new();
236        expected.insert('a', 2);
237        expected.insert('b', 2);
238        expected.insert('c', 1);
239        expected.insert('d', 1);
240        assert_eq!(result, expected);
241    }
242
243    #[test]
244    fn test_count_values_with_nan_floats() {
245        let float_collection = vec![
246            common::Float(std::f64::NAN),
247            common::Float(std::f64::INFINITY),
248            common::Float(std::f64::NAN),
249            common::Float(1.0),
250        ];
251        let result = count_values(&float_collection);
252        let mut expected = HashMap::new();
253        expected.insert(common::Float(std::f64::NAN), 2);
254        expected.insert(common::Float(std::f64::INFINITY), 1);
255        expected.insert(common::Float(1.0), 1);
256        // Note: HashMap treats different NaN representations as distinct keys
257        assert_eq!(result.get(&common::Float(std::f64::NAN)), Some(&2));
258        assert_eq!(result.get(&common::Float(std::f64::INFINITY)), Some(&1));
259        assert_eq!(result.get(&common::Float(1.0)), Some(&1));
260    }
261}