blob: be22ec849e3282f08e558957377aa031596e44cc [file] [log] [blame]
Joel Galenson6f798712021-04-01 17:03:06 -07001#![cfg(feature = "use_std")]
2
3use crate::MinMaxResult;
4use std::collections::HashMap;
5use std::cmp::Ordering;
6use std::hash::Hash;
7use std::iter::Iterator;
8use std::ops::{Add, Mul};
9
Joel Galensonb593e252021-06-21 13:15:57 -070010/// A wrapper to allow for an easy [`into_grouping_map_by`](crate::Itertools::into_grouping_map_by)
Joel Galenson6f798712021-04-01 17:03:06 -070011#[derive(Clone, Debug)]
12pub struct MapForGrouping<I, F>(I, F);
13
14impl<I, F> MapForGrouping<I, F> {
15 pub(crate) fn new(iter: I, key_mapper: F) -> Self {
16 Self(iter, key_mapper)
17 }
18}
19
20impl<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);
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`
32pub 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///
Joel Galensonb593e252021-06-21 13:15:57 -070041/// See [`GroupingMap`] for more informations.
Joel Galenson6f798712021-04-01 17:03:06 -070042#[must_use = "GroupingMapBy is lazy and do nothing unless consumed"]
43pub 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"]
52pub struct GroupingMap<I> {
53 iter: I,
54}
55
56impl<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 /// ```
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
Joel Galensonb593e252021-06-21 13:15:57 -0700105 self.iter.for_each(|(key, val)| {
Joel Galenson6f798712021-04-01 17:03:06 -0700106 let acc = destination_map.remove(&key);
107 if let Some(op_res) = operation(acc, &key, val) {
108 destination_map.insert(key, op_res);
109 }
Joel Galensonb593e252021-06-21 13:15:57 -0700110 });
Joel Galenson6f798712021-04-01 17:03:06 -0700111
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 /// ```
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 ///
Joel Galensonb593e252021-06-21 13:15:57 -0700163 /// [`fold`]: GroupingMap::fold
Joel Galenson6f798712021-04-01 17:03:06 -0700164 ///
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 /// ```
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 /// ```
206 pub fn collect<C>(self) -> HashMap<K, C>
207 where C: Default + Extend<V>,
208 {
209 let mut destination_map = HashMap::new();
210
Joel Galensonb593e252021-06-21 13:15:57 -0700211 self.iter.for_each(|(key, val)| {
Joel Galenson6f798712021-04-01 17:03:06 -0700212 destination_map.entry(key).or_insert_with(C::default).extend(Some(val));
Joel Galensonb593e252021-06-21 13:15:57 -0700213 });
Joel Galenson6f798712021-04-01 17:03:06 -0700214
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 /// ```
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 /// ```
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 /// ```
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 /// ```
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 /// ```
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 /// ```
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 ///
Joel Galensonb593e252021-06-21 13:15:57 -0700380 /// See [.minmax()](crate::Itertools::minmax) for the non-grouping version.
Joel Galenson6f798712021-04-01 17:03:06 -0700381 ///
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 /// ```
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 /// ```
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 /// ```
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 /// ```
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 /// ```
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}