grouping_by/lib.rs
1//! This library provides users the possibility of grouping your iterators of various ways.
2//! It is similar to Java `Collectors.groupingBy`
3//!
4//! It is implemented for any type which implements `Iterator`, but you can implement it to your custom iterator.
5//!
6//! ## Example:
7//! ```rust
8//! use std::collections::HashMap;
9//! use crate::grouping_by::GroupingBy;
10//!
11//! #[derive(Debug, PartialEq)]
12//! struct Point {
13//! x: i32,
14//! y: i32,
15//! }
16//! let array: [Point; 4] = [
17//! Point { x: 1, y: 2 },
18//! Point { x: 1, y: 3 },
19//! Point { x: 2, y: 2 },
20//! Point { x: 2, y: 2 },
21//! ];
22//!
23//! assert_eq!(
24//! [
25//! (1, vec![&Point { x: 1, y: 2 }, &Point { x: 1, y: 3 }]),
26//! (2, vec![&Point { x: 2, y: 2 }, &Point { x: 2, y: 2 }])
27//! ]
28//! .iter()
29//! .cloned()
30//! .collect::<HashMap<i32, Vec<&Point>>>(),
31//! array.iter().grouping_by(|point| point.x)
32//! );
33//! ```
34// TODO
35// Implement two argument grouping by, just like groupingBy of Java does
36
37use std::collections::{
38 hash_map::{Entry, HashMap},
39 HashSet,
40};
41use std::hash::Hash;
42
43pub trait GroupingBy {
44 /// The type of the Item of the iterator
45 type GItem;
46
47 /// Group by the key function given as parameter.
48 /// The keys are the different values that the function can return,
49 /// and the values are a `Vec` with the items of the iterator which has the key as property
50 ///
51 /// ## Example
52 /// ```rust
53 /// # use crate::grouping_by::GroupingBy;
54 /// # use std::collections::{HashSet, HashMap};
55 ///
56 /// let numbers_grouped = [-1i8, -2, 1, 2]
57 /// .iter()
58 /// .grouping_by_as_set(|number| number.abs());
59 ///
60 /// assert_eq!(
61 /// numbers_grouped,
62 /// [(1, [1, -1].iter().collect()), (2, [2, -2].iter().collect())]
63 /// .iter()
64 /// .cloned()
65 /// .collect::<HashMap<i8, HashSet<&i8>>>()
66 /// );
67 /// ```
68 fn grouping_by<K, F>(self, key: F) -> HashMap<K, Vec<Self::GItem>>
69 where
70 F: FnMut(&Self::GItem) -> K,
71 K: Eq + Hash;
72
73 /// Group by the key function given as parameter.
74 /// The keys are the different values that the function can return,
75 /// and the values are a `HashSet` with the items of the iterator which has the key as property
76 ///
77 /// ## Example
78 /// ```rust
79 /// # use crate::grouping_by::GroupingBy;
80 /// # use std::collections::{HashSet, HashMap};
81 ///
82 /// let numbers_grouped = [-1i8, -2, 1, 2]
83 /// .iter()
84 /// .grouping_by(|number| number.abs());
85 ///
86 /// assert_eq!(
87 /// numbers_grouped,
88 /// [(2, vec![&-2, &2]), (1, vec![&-1, &1])]
89 /// .iter()
90 /// .cloned()
91 /// .collect()
92 /// );
93 /// ```
94 fn grouping_by_as_set<K, F>(self, key: F) -> HashMap<K, HashSet<Self::GItem>>
95 where
96 Self::GItem: Eq + Hash,
97 F: FnMut(&Self::GItem) -> K,
98 K: Eq + Hash;
99
100 /// Count the elements of the iterator given a function
101 ///
102 /// ## Example
103 /// ```rust
104 /// # use crate::grouping_by::GroupingBy;
105 /// # use std::collections::{HashSet, HashMap};
106 /// let numbers_counted = [1, 2, 2, 3, 4].iter().counter(|&&x| x);
107 ///
108 /// assert_eq!(
109 /// numbers_counted,
110 /// [(1, 1), (2, 2), (3, 1), (4, 1)]
111 /// .iter()
112 /// .cloned()
113 /// .collect::<HashMap<i8, usize>>()
114 /// )
115 /// ```
116 fn counter<K, F>(self, key: F) -> HashMap<K, usize>
117 where
118 K: Eq + Hash,
119 F: FnMut(&Self::GItem) -> K;
120
121 /// Given a functions F and C compute the maximum of the elements given a comparator and a finisher.
122 ///
123 /// Params:
124 ///
125 /// `key` -> function to create the keys of the resulting map
126 ///
127 /// `comparator` -> function to get the max value
128 ///
129 /// ## Example:
130 ///
131 /// ```rust
132 /// # use crate::grouping_by::GroupingBy;
133 ///
134 /// #[derive(Debug, Clone, PartialEq)]
135 /// struct Vector {
136 /// x: i32,
137 /// y: i32,
138 /// z: i32
139 /// }
140 ///
141 /// const BAR: [Vector; 4] = [
142 /// Vector { x: 1, y: 2, z: 4 },
143 /// Vector { x: 1, y: 3, z: 3 },
144 /// Vector { x: 2, y: 2, z: 2 },
145 /// Vector { x: 2, y: 2, z: 1 },
146 /// ];
147 ///
148 /// // Return a HashMap with the `y` fields as keys
149 /// // and the `z` fields of the vectors with that key with the maximum `x`
150 ///
151 /// let a = BAR.iter().grouping_by_max(
152 /// |vector| vector.y,
153 /// |vector1, vector2| vector1.x.cmp(&vector2.x)
154 /// );
155 /// assert_eq!(a, [(3, &Vector { x: 1, y: 3, z: 3 } ), (2, &Vector { x: 2, y: 2, z: 2 })].iter().cloned().collect())
156 /// ```
157 fn grouping_by_max<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
158 where
159 K: Eq + Hash,
160 F: FnMut(&Self::GItem) -> K,
161 C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering;
162
163 /// Given a functions F, C and compute the maximum of the elements given a comparator and a finisher.
164 ///
165 /// Params:
166 ///
167 /// `key` -> function to create the keys of the resulting map
168 ///
169 /// `comparator` -> function to get the max value
170 ///
171 /// ## Example:
172 ///
173 /// ```rust
174 /// # use crate::grouping_by::GroupingBy;
175 ///
176 /// #[derive(Debug, Clone, PartialEq)]
177 /// struct Vector {
178 /// x: i32,
179 /// y: i32,
180 /// z: i32
181 /// }
182 ///
183 /// const BAR: [Vector; 4] = [
184 /// Vector { x: 1, y: 2, z: 4 },
185 /// Vector { x: 1, y: 3, z: 3 },
186 /// Vector { x: 2, y: 2, z: 2 },
187 /// Vector { x: 2, y: 2, z: 1 },
188 /// ];
189 ///
190 /// // Return a HashMap with the `y` fields as keys
191 /// // and the `z` fields of the vectors with that key with the minimum `x`
192 ///
193 /// let a = BAR.iter().grouping_by_min(
194 /// |vector| vector.y,
195 /// |vector1, vector2| vector1.x.cmp(&vector2.x),
196 /// );
197 /// assert_eq!(a, [(3, &Vector { x: 1, y: 3, z: 3 } ), (2, &Vector { x: 1, y: 2, z: 4 })].iter().cloned().collect())
198 /// ```
199 fn grouping_by_min<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
200 where
201 K: Eq + Hash,
202 F: FnMut(&Self::GItem) -> K,
203 C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering;
204
205 /// Return a map containing the sum of the values of a given key both obtained by provided as input functions.
206 ///
207 /// Params:
208 ///
209 /// `key` -> function to create the keys of the resulting map
210 ///
211 /// `value` -> function to get the values to sum
212 ///
213 /// ## Example:
214 ///
215 /// ```rust
216 /// # use crate::grouping_by::GroupingBy;
217 /// struct Vector {
218 /// x: i32,
219 /// y: i32,
220 /// z: i32
221 /// }
222 ///
223 /// const BAR: [Vector; 4] = [
224 /// Vector { x: 1, y: 2, z: 4 },
225 /// Vector { x: 1, y: 3, z: 3 },
226 /// Vector { x: 2, y: 2, z: 2 },
227 /// Vector { x: 2, y: 2, z: 1 },
228 /// ];
229 ///
230 /// let a = BAR.iter().summing(
231 /// |vector| vector.x,
232 /// |vector| vector.y
233 /// );
234 /// assert_eq!(a, [(2, 4), (1, 5)].iter().cloned().collect())
235 /// ```
236 fn summing<K, V, F, G>(self, key: F, sum_func: G) -> HashMap<K, V>
237 where
238 K: Eq + Hash,
239 F: FnMut(&Self::GItem) -> K,
240 G: FnMut(&Self::GItem) -> V,
241 V: Default + std::ops::AddAssign;
242}
243
244mod utilities {
245 use super::{Entry, Hash, HashMap};
246
247 pub fn grouping_by_min_max_aux<T, K, F, C>(
248 iterator: T,
249 mut key: F,
250 mut comparator: C,
251 type_ord: std::cmp::Ordering,
252 ) -> HashMap<K, T::Item>
253 where
254 T: Iterator,
255 K: Eq + Hash,
256 F: FnMut(&T::Item) -> K,
257 C: FnMut(&T::Item, &T::Item) -> std::cmp::Ordering,
258 {
259 let mut map = HashMap::new();
260 iterator.for_each(|item| {
261 let key = key(&item);
262 match map.entry(key) {
263 Entry::Occupied(mut entry) => {
264 if comparator(&item, entry.get()) == type_ord {
265 entry.insert(item);
266 }
267 }
268 Entry::Vacant(entry) => {
269 entry.insert(item);
270 }
271 }
272 });
273 map
274 }
275}
276
277impl<T: Iterator> GroupingBy for T {
278 type GItem = T::Item;
279 fn grouping_by<K, F>(self, mut key: F) -> HashMap<K, Vec<Self::GItem>>
280 where
281 F: FnMut(&Self::GItem) -> K,
282 K: Eq + Hash,
283 {
284 let mut map = HashMap::new();
285 self.for_each(|item| {
286 map.entry(key(&item)).or_insert_with(Vec::new).push(item);
287 });
288 map
289 }
290 fn grouping_by_as_set<K, F>(self, mut key: F) -> HashMap<K, HashSet<Self::GItem>>
291 where
292 Self::GItem: Eq + Hash,
293 F: FnMut(&Self::GItem) -> K,
294 K: Eq + Hash,
295 {
296 let mut map = HashMap::new();
297 self.for_each(|item| {
298 map.entry(key(&item))
299 .or_insert_with(HashSet::new)
300 .insert(item);
301 });
302 map
303 }
304 fn counter<K, F>(self, mut key: F) -> HashMap<K, usize>
305 where
306 K: Eq + Hash,
307 F: FnMut(&Self::GItem) -> K,
308 {
309 let mut map = HashMap::new();
310 self.for_each(|item| {
311 *map.entry(key(&item)).or_insert(0) += 1;
312 });
313 map
314 }
315
316 fn grouping_by_max<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
317 where
318 K: Eq + Hash,
319 F: FnMut(&Self::GItem) -> K,
320 C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering,
321 {
322 utilities::grouping_by_min_max_aux(self, key, comparator, std::cmp::Ordering::Greater)
323 }
324
325 fn grouping_by_min<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
326 where
327 K: Eq + Hash,
328 F: FnMut(&Self::GItem) -> K,
329 C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering,
330 {
331 utilities::grouping_by_min_max_aux(self, key, comparator, std::cmp::Ordering::Less)
332 }
333
334 fn summing<K, V, F, G>(self, mut key: F, mut value: G) -> HashMap<K, V>
335 where
336 K: Eq + Hash,
337 F: FnMut(&Self::GItem) -> K,
338 G: FnMut(&Self::GItem) -> V,
339 V: Default + std::ops::AddAssign,
340 {
341 let mut map: HashMap<K, V> = HashMap::new();
342 self.for_each(|item| {
343 let v = map.entry(key(&item)).or_default();
344 *v += value(&item);
345 });
346 map
347 }
348}