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