1 #![cfg(feature = "use_std")]
2 
3 use crate::MinMaxResult;
4 use std::collections::HashMap;
5 use std::cmp::Ordering;
6 use std::hash::Hash;
7 use std::iter::Iterator;
8 use std::ops::{Add, Mul};
9 
10 /// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
11 #[derive(Clone, Debug)]
12 pub struct MapForGrouping<I, F>(I, F);
13 
14 impl<I, F> MapForGrouping<I, F> {
new(iter: I, key_mapper: F) -> Self15     pub(crate) fn new(iter: I, key_mapper: F) -> Self {
16         Self(iter, key_mapper)
17     }
18 }
19 
20 impl<K, V, I, F> Iterator for MapForGrouping<I, F>
21     where I: Iterator<Item = V>,
22           K: Hash + Eq,
23           F: FnMut(&V) -> K,
24 {
25     type Item = (K, V);
next(&mut self) -> Option<Self::Item>26     fn next(&mut self) -> Option<Self::Item> {
27         self.0.next().map(|val| ((self.1)(&val), val))
28     }
29 }
30 
31 /// Creates a new `GroupingMap` from `iter`
new<I, K, V>(iter: I) -> GroupingMap<I> where I: Iterator<Item = (K, V)>, K: Hash + Eq,32 pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
33     where I: Iterator<Item = (K, V)>,
34           K: Hash + Eq,
35 {
36     GroupingMap { iter }
37 }
38 
39 /// `GroupingMapBy` is an intermediate struct for efficient group-and-fold operations.
40 ///
41 /// See [`GroupingMap`] for more informations.
42 #[must_use = "GroupingMapBy is lazy and do nothing unless consumed"]
43 pub type GroupingMapBy<I, F> = GroupingMap<MapForGrouping<I, F>>;
44 
45 /// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
46 /// It groups elements by their key and at the same time fold each group
47 /// using some aggregating operation.
48 ///
49 /// No method on this struct performs temporary allocations.
50 #[derive(Clone, Debug)]
51 #[must_use = "GroupingMap is lazy and do nothing unless consumed"]
52 pub struct GroupingMap<I> {
53     iter: I,
54 }
55 
56 impl<I, K, V> GroupingMap<I>
57     where I: Iterator<Item = (K, V)>,
58           K: Hash + Eq,
59 {
60     /// This is the generic way to perform any operation on a `GroupingMap`.
61     /// It's suggested to use this method only to implement custom operations
62     /// when the already provided ones are not enough.
63     ///
64     /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
65     /// of each group sequentially, passing the previously accumulated value, a reference to the key
66     /// and the current element as arguments, and stores the results in an `HashMap`.
67     ///
68     /// The `operation` function is invoked on each element with the following parameters:
69     ///  - the current value of the accumulator of the group if there is currently one;
70     ///  - a reference to the key of the group this element belongs to;
71     ///  - the element from the source being aggregated;
72     ///
73     /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
74     /// otherwise the previous accumulation is discarded.
75     ///
76     /// Return a `HashMap` associating the key of each group with the result of aggregation of
77     /// that group's elements. If the aggregation of the last element of a group discards the
78     /// accumulator then there won't be an entry associated to that group's key.
79     ///
80     /// ```
81     /// use itertools::Itertools;
82     ///
83     /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
84     /// let lookup = data.into_iter()
85     ///     .into_grouping_map_by(|&n| n % 4)
86     ///     .aggregate(|acc, _key, val| {
87     ///         if val == 0 || val == 10 {
88     ///             None
89     ///         } else {
90     ///             Some(acc.unwrap_or(0) + val)
91     ///         }
92     ///     });
93     ///
94     /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
95     /// assert_eq!(lookup[&1], 5 + 9);
96     /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
97     /// assert_eq!(lookup[&3], 7);
98     /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
99     /// ```
aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R> where FO: FnMut(Option<R>, &K, V) -> Option<R>,100     pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
101         where FO: FnMut(Option<R>, &K, V) -> Option<R>,
102     {
103         let mut destination_map = HashMap::new();
104 
105         self.iter.for_each(|(key, val)| {
106             let acc = destination_map.remove(&key);
107             if let Some(op_res) = operation(acc, &key, val) {
108                 destination_map.insert(key, op_res);
109             }
110         });
111 
112         destination_map
113     }
114 
115     /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
116     /// of each group sequentially, passing the previously accumulated value, a reference to the key
117     /// and the current element as arguments, and stores the results in a new map.
118     ///
119     /// `init` is the value from which will be cloned the initial value of each accumulator.
120     ///
121     /// `operation` is a function that is invoked on each element with the following parameters:
122     ///  - the current value of the accumulator of the group;
123     ///  - a reference to the key of the group this element belongs to;
124     ///  - the element from the source being accumulated.
125     ///
126     /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
127     ///
128     /// ```
129     /// use itertools::Itertools;
130     ///
131     /// let lookup = (1..=7)
132     ///     .into_grouping_map_by(|&n| n % 3)
133     ///     .fold(0, |acc, _key, val| acc + val);
134     ///
135     /// assert_eq!(lookup[&0], 3 + 6);
136     /// assert_eq!(lookup[&1], 1 + 4 + 7);
137     /// assert_eq!(lookup[&2], 2 + 5);
138     /// assert_eq!(lookup.len(), 3);
139     /// ```
fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R> where R: Clone, FO: FnMut(R, &K, V) -> R,140     pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
141         where R: Clone,
142               FO: FnMut(R, &K, V) -> R,
143     {
144         self.aggregate(|acc, key, val| {
145             let acc = acc.unwrap_or_else(|| init.clone());
146             Some(operation(acc, key, val))
147         })
148     }
149 
150     /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
151     /// of each group sequentially, passing the previously accumulated value, a reference to the key
152     /// and the current element as arguments, and stores the results in a new map.
153     ///
154     /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group.
155     ///
156     /// `operation` is a function that is invoked on each element with the following parameters:
157     ///  - the current value of the accumulator of the group;
158     ///  - a reference to the key of the group this element belongs to;
159     ///  - the element from the source being accumulated.
160     ///
161     /// Return a `HashMap` associating the key of each group with the result of folding that group's elements.
162     ///
163     /// [`fold`]: GroupingMap::fold
164     ///
165     /// ```
166     /// use itertools::Itertools;
167     ///
168     /// let lookup = (1..=7)
169     ///     .into_grouping_map_by(|&n| n % 3)
170     ///     .fold_first(|acc, _key, val| acc + val);
171     ///
172     /// assert_eq!(lookup[&0], 3 + 6);
173     /// assert_eq!(lookup[&1], 1 + 4 + 7);
174     /// assert_eq!(lookup[&2], 2 + 5);
175     /// assert_eq!(lookup.len(), 3);
176     /// ```
fold_first<FO>(self, mut operation: FO) -> HashMap<K, V> where FO: FnMut(V, &K, V) -> V,177     pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
178         where FO: FnMut(V, &K, V) -> V,
179     {
180         self.aggregate(|acc, key, val| {
181             Some(match acc {
182                 Some(acc) => operation(acc, key, val),
183                 None => val,
184             })
185         })
186     }
187 
188     /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
189     /// an instance of `C`. The iteration order is preserved when inserting elements.
190     ///
191     /// Return a `HashMap` associating the key of each group with the collection containing that group's elements.
192     ///
193     /// ```
194     /// use itertools::Itertools;
195     /// use std::collections::HashSet;
196     ///
197     /// let lookup = vec![0, 1, 2, 3, 4, 5, 6, 2, 3, 6].into_iter()
198     ///     .into_grouping_map_by(|&n| n % 3)
199     ///     .collect::<HashSet<_>>();
200     ///
201     /// assert_eq!(lookup[&0], vec![0, 3, 6].into_iter().collect::<HashSet<_>>());
202     /// assert_eq!(lookup[&1], vec![1, 4].into_iter().collect::<HashSet<_>>());
203     /// assert_eq!(lookup[&2], vec![2, 5].into_iter().collect::<HashSet<_>>());
204     /// assert_eq!(lookup.len(), 3);
205     /// ```
collect<C>(self) -> HashMap<K, C> where C: Default + Extend<V>,206     pub fn collect<C>(self) -> HashMap<K, C>
207         where C: Default + Extend<V>,
208     {
209         let mut destination_map = HashMap::new();
210 
211         self.iter.for_each(|(key, val)| {
212             destination_map.entry(key).or_insert_with(C::default).extend(Some(val));
213         });
214 
215         destination_map
216     }
217 
218     /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group.
219     ///
220     /// If several elements are equally maximum, the last element is picked.
221     ///
222     /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
223     ///
224     /// ```
225     /// use itertools::Itertools;
226     ///
227     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
228     ///     .into_grouping_map_by(|&n| n % 3)
229     ///     .max();
230     ///
231     /// assert_eq!(lookup[&0], 12);
232     /// assert_eq!(lookup[&1], 7);
233     /// assert_eq!(lookup[&2], 8);
234     /// assert_eq!(lookup.len(), 3);
235     /// ```
max(self) -> HashMap<K, V> where V: Ord,236     pub fn max(self) -> HashMap<K, V>
237         where V: Ord,
238     {
239         self.max_by(|_, v1, v2| V::cmp(v1, v2))
240     }
241 
242     /// Groups elements from the `GroupingMap` source by key and finds the maximum of each group
243     /// with respect to the specified comparison function.
244     ///
245     /// If several elements are equally maximum, the last element is picked.
246     ///
247     /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
248     ///
249     /// ```
250     /// use itertools::Itertools;
251     ///
252     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
253     ///     .into_grouping_map_by(|&n| n % 3)
254     ///     .max_by(|_key, x, y| y.cmp(x));
255     ///
256     /// assert_eq!(lookup[&0], 3);
257     /// assert_eq!(lookup[&1], 1);
258     /// assert_eq!(lookup[&2], 5);
259     /// assert_eq!(lookup.len(), 3);
260     /// ```
max_by<F>(self, mut compare: F) -> HashMap<K, V> where F: FnMut(&K, &V, &V) -> Ordering,261     pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
262         where F: FnMut(&K, &V, &V) -> Ordering,
263     {
264         self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
265             Ordering::Less | Ordering::Equal => val,
266             Ordering::Greater => acc
267         })
268     }
269 
270     /// Groups elements from the `GroupingMap` source by key and finds the element of each group
271     /// that gives the maximum from the specified function.
272     ///
273     /// If several elements are equally maximum, the last element is picked.
274     ///
275     /// Returns a `HashMap` associating the key of each group with the maximum of that group's elements.
276     ///
277     /// ```
278     /// use itertools::Itertools;
279     ///
280     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
281     ///     .into_grouping_map_by(|&n| n % 3)
282     ///     .max_by_key(|_key, &val| val % 4);
283     ///
284     /// assert_eq!(lookup[&0], 3);
285     /// assert_eq!(lookup[&1], 7);
286     /// assert_eq!(lookup[&2], 5);
287     /// assert_eq!(lookup.len(), 3);
288     /// ```
max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V> where F: FnMut(&K, &V) -> CK, CK: Ord,289     pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
290         where F: FnMut(&K, &V) -> CK,
291               CK: Ord,
292     {
293         self.max_by(|key, v1, v2| f(key, &v1).cmp(&f(key, &v2)))
294     }
295 
296     /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group.
297     ///
298     /// If several elements are equally minimum, the first element is picked.
299     ///
300     /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
301     ///
302     /// ```
303     /// use itertools::Itertools;
304     ///
305     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
306     ///     .into_grouping_map_by(|&n| n % 3)
307     ///     .min();
308     ///
309     /// assert_eq!(lookup[&0], 3);
310     /// assert_eq!(lookup[&1], 1);
311     /// assert_eq!(lookup[&2], 5);
312     /// assert_eq!(lookup.len(), 3);
313     /// ```
min(self) -> HashMap<K, V> where V: Ord,314     pub fn min(self) -> HashMap<K, V>
315         where V: Ord,
316     {
317         self.min_by(|_, v1, v2| V::cmp(v1, v2))
318     }
319 
320     /// Groups elements from the `GroupingMap` source by key and finds the minimum of each group
321     /// with respect to the specified comparison function.
322     ///
323     /// If several elements are equally minimum, the first element is picked.
324     ///
325     /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
326     ///
327     /// ```
328     /// use itertools::Itertools;
329     ///
330     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
331     ///     .into_grouping_map_by(|&n| n % 3)
332     ///     .min_by(|_key, x, y| y.cmp(x));
333     ///
334     /// assert_eq!(lookup[&0], 12);
335     /// assert_eq!(lookup[&1], 7);
336     /// assert_eq!(lookup[&2], 8);
337     /// assert_eq!(lookup.len(), 3);
338     /// ```
min_by<F>(self, mut compare: F) -> HashMap<K, V> where F: FnMut(&K, &V, &V) -> Ordering,339     pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
340         where F: FnMut(&K, &V, &V) -> Ordering,
341     {
342         self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
343             Ordering::Less | Ordering::Equal => acc,
344             Ordering::Greater => val
345         })
346     }
347 
348     /// Groups elements from the `GroupingMap` source by key and finds the element of each group
349     /// that gives the minimum from the specified function.
350     ///
351     /// If several elements are equally minimum, the first element is picked.
352     ///
353     /// Returns a `HashMap` associating the key of each group with the minimum of that group's elements.
354     ///
355     /// ```
356     /// use itertools::Itertools;
357     ///
358     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
359     ///     .into_grouping_map_by(|&n| n % 3)
360     ///     .min_by_key(|_key, &val| val % 4);
361     ///
362     /// assert_eq!(lookup[&0], 12);
363     /// assert_eq!(lookup[&1], 4);
364     /// assert_eq!(lookup[&2], 8);
365     /// assert_eq!(lookup.len(), 3);
366     /// ```
min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V> where F: FnMut(&K, &V) -> CK, CK: Ord,367     pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
368         where F: FnMut(&K, &V) -> CK,
369               CK: Ord,
370     {
371         self.min_by(|key, v1, v2| f(key, &v1).cmp(&f(key, &v2)))
372     }
373 
374     /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of
375     /// each group.
376     ///
377     /// If several elements are equally maximum, the last element is picked.
378     /// If several elements are equally minimum, the first element is picked.
379     ///
380     /// See [.minmax()](crate::Itertools::minmax) for the non-grouping version.
381     ///
382     /// Differences from the non grouping version:
383     /// - It never produces a `MinMaxResult::NoElements`
384     /// - It doesn't have any speedup
385     ///
386     /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
387     ///
388     /// ```
389     /// use itertools::Itertools;
390     /// use itertools::MinMaxResult::{OneElement, MinMax};
391     ///
392     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
393     ///     .into_grouping_map_by(|&n| n % 3)
394     ///     .minmax();
395     ///
396     /// assert_eq!(lookup[&0], MinMax(3, 12));
397     /// assert_eq!(lookup[&1], MinMax(1, 7));
398     /// assert_eq!(lookup[&2], OneElement(5));
399     /// assert_eq!(lookup.len(), 3);
400     /// ```
minmax(self) -> HashMap<K, MinMaxResult<V>> where V: Ord,401     pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
402         where V: Ord,
403     {
404         self.minmax_by(|_, v1, v2| V::cmp(v1, v2))
405     }
406 
407     /// Groups elements from the `GroupingMap` source by key and find the maximum and minimum of
408     /// each group with respect to the specified comparison function.
409     ///
410     /// If several elements are equally maximum, the last element is picked.
411     /// If several elements are equally minimum, the first element is picked.
412     ///
413     /// It has the same differences from the non-grouping version as `minmax`.
414     ///
415     /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
416     ///
417     /// ```
418     /// use itertools::Itertools;
419     /// use itertools::MinMaxResult::{OneElement, MinMax};
420     ///
421     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
422     ///     .into_grouping_map_by(|&n| n % 3)
423     ///     .minmax_by(|_key, x, y| y.cmp(x));
424     ///
425     /// assert_eq!(lookup[&0], MinMax(12, 3));
426     /// assert_eq!(lookup[&1], MinMax(7, 1));
427     /// assert_eq!(lookup[&2], OneElement(5));
428     /// assert_eq!(lookup.len(), 3);
429     /// ```
minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>> where F: FnMut(&K, &V, &V) -> Ordering,430     pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
431         where F: FnMut(&K, &V, &V) -> Ordering,
432     {
433         self.aggregate(|acc, key, val| {
434             Some(match acc {
435                 Some(MinMaxResult::OneElement(e)) => {
436                     if compare(key, &val, &e) == Ordering::Less {
437                         MinMaxResult::MinMax(val, e)
438                     } else {
439                         MinMaxResult::MinMax(e, val)
440                     }
441                 }
442                 Some(MinMaxResult::MinMax(min, max)) => {
443                     if compare(key, &val, &min) == Ordering::Less {
444                         MinMaxResult::MinMax(val, max)
445                     } else if compare(key, &val, &max) != Ordering::Less {
446                         MinMaxResult::MinMax(min, val)
447                     } else {
448                         MinMaxResult::MinMax(min, max)
449                     }
450                 }
451                 None => MinMaxResult::OneElement(val),
452                 Some(MinMaxResult::NoElements) => unreachable!(),
453             })
454         })
455     }
456 
457     /// Groups elements from the `GroupingMap` source by key and find the elements of each group
458     /// that gives the minimum and maximum from the specified function.
459     ///
460     /// If several elements are equally maximum, the last element is picked.
461     /// If several elements are equally minimum, the first element is picked.
462     ///
463     /// It has the same differences from the non-grouping version as `minmax`.
464     ///
465     /// Returns a `HashMap` associating the key of each group with the minimum and maximum of that group's elements.
466     ///
467     /// ```
468     /// use itertools::Itertools;
469     /// use itertools::MinMaxResult::{OneElement, MinMax};
470     ///
471     /// let lookup = vec![1, 3, 4, 5, 7, 9, 12].into_iter()
472     ///     .into_grouping_map_by(|&n| n % 3)
473     ///     .minmax_by_key(|_key, &val| val % 4);
474     ///
475     /// assert_eq!(lookup[&0], MinMax(12, 3));
476     /// assert_eq!(lookup[&1], MinMax(4, 7));
477     /// assert_eq!(lookup[&2], OneElement(5));
478     /// assert_eq!(lookup.len(), 3);
479     /// ```
minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>> where F: FnMut(&K, &V) -> CK, CK: Ord,480     pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
481         where F: FnMut(&K, &V) -> CK,
482               CK: Ord,
483     {
484         self.minmax_by(|key, v1, v2| f(key, &v1).cmp(&f(key, &v2)))
485     }
486 
487     /// Groups elements from the `GroupingMap` source by key and sums them.
488     ///
489     /// This is just a shorthand for `self.fold_first(|acc, _, val| acc + val)`.
490     /// It is more limited than `Iterator::sum` since it doesn't use the `Sum` trait.
491     ///
492     /// Returns a `HashMap` associating the key of each group with the sum of that group's elements.
493     ///
494     /// ```
495     /// use itertools::Itertools;
496     ///
497     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
498     ///     .into_grouping_map_by(|&n| n % 3)
499     ///     .sum();
500     ///
501     /// assert_eq!(lookup[&0], 3 + 9 + 12);
502     /// assert_eq!(lookup[&1], 1 + 4 + 7);
503     /// assert_eq!(lookup[&2], 5 + 8);
504     /// assert_eq!(lookup.len(), 3);
505     /// ```
sum(self) -> HashMap<K, V> where V: Add<V, Output = V>506     pub fn sum(self) -> HashMap<K, V>
507         where V: Add<V, Output = V>
508     {
509         self.fold_first(|acc, _, val| acc + val)
510     }
511 
512     /// Groups elements from the `GroupingMap` source by key and multiply them.
513     ///
514     /// This is just a shorthand for `self.fold_first(|acc, _, val| acc * val)`.
515     /// It is more limited than `Iterator::product` since it doesn't use the `Product` trait.
516     ///
517     /// Returns a `HashMap` associating the key of each group with the product of that group's elements.
518     ///
519     /// ```
520     /// use itertools::Itertools;
521     ///
522     /// let lookup = vec![1, 3, 4, 5, 7, 8, 9, 12].into_iter()
523     ///     .into_grouping_map_by(|&n| n % 3)
524     ///     .product();
525     ///
526     /// assert_eq!(lookup[&0], 3 * 9 * 12);
527     /// assert_eq!(lookup[&1], 1 * 4 * 7);
528     /// assert_eq!(lookup[&2], 5 * 8);
529     /// assert_eq!(lookup.len(), 3);
530     /// ```
product(self) -> HashMap<K, V> where V: Mul<V, Output = V>,531     pub fn product(self) -> HashMap<K, V>
532         where V: Mul<V, Output = V>,
533     {
534         self.fold_first(|acc, _, val| acc * val)
535     }
536 }
537