Coverage Report

Created: 2023-04-25 07:07

/rust/registry/src/index.crates.io-6f17d22bba15001f/itertools-0.10.3/src/grouping_map.rs
Line
Count
Source (jump to first uncovered line)
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
0
#[derive(Clone, Debug)]
12
pub struct MapForGrouping<I, F>(I, F);
13
14
impl<I, F> MapForGrouping<I, F> {
15
0
    pub(crate) fn new(iter: I, key_mapper: F) -> Self {
16
0
        Self(iter, key_mapper)
17
0
    }
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);
26
0
    fn next(&mut self) -> Option<Self::Item> {
27
0
        self.0.next().map(|val| ((self.1)(&val), val))
28
0
    }
29
}
30
31
/// Creates a new `GroupingMap` from `iter`
32
0
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
33
0
    where I: Iterator<Item = (K, V)>,
34
0
          K: Hash + Eq,
35
0
{
36
0
    GroupingMap { iter }
37
0
}
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
0
#[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
    /// ```
100
0
    pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
101
0
        where FO: FnMut(Option<R>, &K, V) -> Option<R>,
102
0
    {
103
0
        let mut destination_map = HashMap::new();
104
0
105
0
        self.iter.for_each(|(key, val)| {
106
0
            let acc = destination_map.remove(&key);
107
0
            if let Some(op_res) = operation(acc, &key, val) {
108
0
                destination_map.insert(key, op_res);
109
0
            }
110
0
        });
111
0
112
0
        destination_map
113
0
    }
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
0
    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
141
0
        where R: Clone,
142
0
              FO: FnMut(R, &K, V) -> R,
143
0
    {
144
0
        self.aggregate(|acc, key, val| {
145
0
            let acc = acc.unwrap_or_else(|| init.clone());
146
0
            Some(operation(acc, key, val))
147
0
        })
148
0
    }
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
    /// ```
177
0
    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
178
0
        where FO: FnMut(V, &K, V) -> V,
179
0
    {
180
0
        self.aggregate(|acc, key, val| {
181
0
            Some(match acc {
182
0
                Some(acc) => operation(acc, key, val),
183
0
                None => val,
184
            })
185
0
        })
186
0
    }
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
0
    pub fn collect<C>(self) -> HashMap<K, C>
207
0
        where C: Default + Extend<V>,
208
0
    {
209
0
        let mut destination_map = HashMap::new();
210
0
211
0
        self.iter.for_each(|(key, val)| {
212
0
            destination_map.entry(key).or_insert_with(C::default).extend(Some(val));
213
0
        });
214
0
215
0
        destination_map
216
0
    }
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
0
    pub fn max(self) -> HashMap<K, V>
237
0
        where V: Ord,
238
0
    {
239
0
        self.max_by(|_, v1, v2| V::cmp(v1, v2))
240
0
    }
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
0
    pub fn max_by<F>(self, mut compare: F) -> HashMap<K, V>
262
0
        where F: FnMut(&K, &V, &V) -> Ordering,
263
0
    {
264
0
        self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
265
0
            Ordering::Less | Ordering::Equal => val,
266
0
            Ordering::Greater => acc
267
0
        })
268
0
    }
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
0
    pub fn max_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
290
0
        where F: FnMut(&K, &V) -> CK,
291
0
              CK: Ord,
292
0
    {
293
0
        self.max_by(|key, v1, v2| f(key, &v1).cmp(&f(key, &v2)))
294
0
    }
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
0
    pub fn min(self) -> HashMap<K, V>
315
0
        where V: Ord,
316
0
    {
317
0
        self.min_by(|_, v1, v2| V::cmp(v1, v2))
318
0
    }
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
0
    pub fn min_by<F>(self, mut compare: F) -> HashMap<K, V>
340
0
        where F: FnMut(&K, &V, &V) -> Ordering,
341
0
    {
342
0
        self.fold_first(|acc, key, val| match compare(key, &acc, &val) {
343
0
            Ordering::Less | Ordering::Equal => acc,
344
0
            Ordering::Greater => val
345
0
        })
346
0
    }
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
0
    pub fn min_by_key<F, CK>(self, mut f: F) -> HashMap<K, V>
368
0
        where F: FnMut(&K, &V) -> CK,
369
0
              CK: Ord,
370
0
    {
371
0
        self.min_by(|key, v1, v2| f(key, &v1).cmp(&f(key, &v2)))
372
0
    }
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
    /// ```
401
0
    pub fn minmax(self) -> HashMap<K, MinMaxResult<V>>
402
0
        where V: Ord,
403
0
    {
404
0
        self.minmax_by(|_, v1, v2| V::cmp(v1, v2))
405
0
    }
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
0
    pub fn minmax_by<F>(self, mut compare: F) -> HashMap<K, MinMaxResult<V>>
431
0
        where F: FnMut(&K, &V, &V) -> Ordering,
432
0
    {
433
0
        self.aggregate(|acc, key, val| {
434
0
            Some(match acc {
435
0
                Some(MinMaxResult::OneElement(e)) => {
436
0
                    if compare(key, &val, &e) == Ordering::Less {
437
0
                        MinMaxResult::MinMax(val, e)
438
                    } else {
439
0
                        MinMaxResult::MinMax(e, val)
440
                    }
441
                }
442
0
                Some(MinMaxResult::MinMax(min, max)) => {
443
0
                    if compare(key, &val, &min) == Ordering::Less {
444
0
                        MinMaxResult::MinMax(val, max)
445
0
                    } else if compare(key, &val, &max) != Ordering::Less {
446
0
                        MinMaxResult::MinMax(min, val)
447
                    } else {
448
0
                        MinMaxResult::MinMax(min, max)
449
                    }
450
                }
451
0
                None => MinMaxResult::OneElement(val),
452
0
                Some(MinMaxResult::NoElements) => unreachable!(),
453
            })
454
0
        })
455
0
    }
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
0
    pub fn minmax_by_key<F, CK>(self, mut f: F) -> HashMap<K, MinMaxResult<V>>
481
0
        where F: FnMut(&K, &V) -> CK,
482
0
              CK: Ord,
483
0
    {
484
0
        self.minmax_by(|key, v1, v2| f(key, &v1).cmp(&f(key, &v2)))
485
0
    }
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
0
    pub fn sum(self) -> HashMap<K, V>
507
0
        where V: Add<V, Output = V>
508
0
    {
509
0
        self.fold_first(|acc, _, val| acc + val)
510
0
    }
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
0
    pub fn product(self) -> HashMap<K, V>
532
0
        where V: Mul<V, Output = V>,
533
0
    {
534
0
        self.fold_first(|acc, _, val| acc * val)
535
0
    }
536
}