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}