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