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