iter_group/lib.rs
1// Copyright 2022 Brian Langenberger
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Extends iterators for grouping their values into a mapping type
10//! whose values are a collection.
11//!
12//! ## Example 1
13//! ```
14//! use std::collections::{BTreeMap, HashMap};
15//! use iter_group::IntoGroup;
16//!
17//! let v = vec![(1, 'a'), (2, 'b'), (3, 'c'), (2, 'd'), (1, 'e'), (1, 'f')];
18//!
19//! let h = v.iter().cloned().group::<HashMap<_, Vec<_>>>();
20//! assert_eq!(h.get(&1), Some(&vec!['a', 'e', 'f']));
21//! assert_eq!(h.get(&2), Some(&vec!['b', 'd']));
22//! assert_eq!(h.get(&3), Some(&vec!['c']));
23//!
24//! let b = v.iter().cloned().group::<BTreeMap<_, Vec<_>>>();
25//! assert_eq!(b.get(&1), Some(&vec!['a', 'e', 'f']));
26//! assert_eq!(b.get(&2), Some(&vec!['b', 'd']));
27//! assert_eq!(b.get(&3), Some(&vec!['c']));
28//! ```
29//!
30//! ## Example 2
31//! ```
32//! use std::collections::{BTreeMap, HashMap};
33//! use iter_group::IntoGroup;
34//!
35//! let v = vec![Some((1, 'a')), Some((2, 'b')), Some((1, 'c'))];
36//!
37//! let h = v.iter().cloned().group::<Option<HashMap<_, Vec<_>>>>().unwrap();
38//! assert_eq!(h.get(&1), Some(&vec!['a', 'c']));
39//! assert_eq!(h.get(&2), Some(&vec!['b']));
40//!
41//! let b = v.iter().cloned().group::<Option<BTreeMap<_, Vec<_>>>>().unwrap();
42//! assert_eq!(b.get(&1), Some(&vec!['a', 'c']));
43//! assert_eq!(b.get(&2), Some(&vec!['b']));
44//!
45//! let v = vec![Some((1, 'a')), None, Some((2, 'b'))];
46//! assert!(v.iter().cloned().group::<Option<HashMap<_, Vec<_>>>>().is_none());
47//! assert!(v.iter().cloned().group::<Option<BTreeMap<_, Vec<_>>>>().is_none());
48//! ```
49//!
50//! ## Example 3
51//! ```
52//! use std::collections::{BTreeMap, HashMap};
53//! use iter_group::IntoGroup;
54//!
55//! let v = vec![Ok((1, 'a')), Ok((2, 'b')), Ok((1, 'c'))];
56//!
57//! let h = v.iter().cloned().group::<Result<HashMap<_, Vec<_>>, ()>>().unwrap();
58//! assert_eq!(h.get(&1), Some(&vec!['a', 'c']));
59//! assert_eq!(h.get(&2), Some(&vec!['b']));
60//!
61//! let b = v.iter().cloned().group::<Result<BTreeMap<_, Vec<_>>, ()>>().unwrap();
62//! assert_eq!(b.get(&1), Some(&vec!['a', 'c']));
63//! assert_eq!(b.get(&2), Some(&vec!['b']));
64//!
65//! let v = vec![Ok((1, 'a')), Err(()), Ok((2, 'b'))];
66//! assert!(v.iter().cloned().group::<Result<HashMap<_, Vec<_>>, ()>>().is_err());
67//! assert!(v.iter().cloned().group::<Result<BTreeMap<_, Vec<_>>, ()>>().is_err());
68//! ```
69//!
70//! Also offers the option to extend iterators for summarizing their values
71//! into a mapping type whose values are a sum.
72//!
73//! # Example 1
74//! ```
75//! use std::collections::{BTreeMap, HashMap};
76//! use iter_group::IntoSummary;
77//!
78//! let v = vec![('a', 1), ('b', 1), ('a', 2), ('b', 2), ('a', 3)];
79//!
80//! let h = v.iter().cloned().summarize::<HashMap<_, _>>();
81//! assert_eq!(h.get(&'a'), Some(&6));
82//! assert_eq!(h.get(&'b'), Some(&3));
83//!
84//! let b = v.iter().cloned().summarize::<BTreeMap<_, _>>();
85//! assert_eq!(b.get(&'a'), Some(&6));
86//! assert_eq!(b.get(&'b'), Some(&3));
87//! ```
88//!
89//! ## Example 2
90//! ```
91//! use std::collections::{BTreeMap, HashMap};
92//! use iter_group::IntoSummary;
93//!
94//! let v = vec![Some(('a', 1)), Some(('b', 2)), Some(('a', 3))];
95//!
96//! let h = v.iter().cloned().summarize::<Option<HashMap<_, _>>>().unwrap();
97//! assert_eq!(h.get(&'a'), Some(&4));
98//! assert_eq!(h.get(&'b'), Some(&2));
99//!
100//! let b = v.iter().cloned().summarize::<Option<BTreeMap<_, _>>>().unwrap();
101//! assert_eq!(b.get(&'a'), Some(&4));
102//! assert_eq!(b.get(&'b'), Some(&2));
103//!
104//! let v = vec![Some(('a', 1)), None, Some(('b', 2))];
105//! assert!(v.iter().cloned().summarize::<Option<HashMap<_, _>>>().is_none());
106//! assert!(v.iter().cloned().summarize::<Option<BTreeMap<_, _>>>().is_none());
107//! ```
108//!
109//! ## Example 3
110//! ```
111//! use std::collections::{BTreeMap, HashMap};
112//! use iter_group::IntoSummary;
113//!
114//! let v = vec![Ok(('a', 1)), Ok(('b', 2)), Ok(('a', 3))];
115//!
116//! let h = v.iter().cloned().summarize::<Result<HashMap<_, _>, ()>>().unwrap();
117//! assert_eq!(h.get(&'a'), Some(&4));
118//! assert_eq!(h.get(&'b'), Some(&2));
119//!
120//! let b = v.iter().cloned().summarize::<Result<BTreeMap<_, _>, ()>>().unwrap();
121//! assert_eq!(b.get(&'a'), Some(&4));
122//! assert_eq!(b.get(&'b'), Some(&2));
123//!
124//! let v = vec![Ok(('a', 1)), Err(()), Ok(('b', 2))];
125//! assert!(v.iter().cloned().summarize::<Result<HashMap<_, _>, ()>>().is_err());
126//! assert!(v.iter().cloned().summarize::<Result<BTreeMap<_, _>, ()>>().is_err());
127//! ```
128
129#![warn(missing_docs)]
130#![forbid(unsafe_code)]
131
132use std::collections::{BTreeMap, HashMap};
133use std::hash::{BuildHasher, Hash};
134
135/// Implemented by mapping types (`HashMap` and `BTreeMap`)
136/// such that they can populated by an iterator.
137/// Roughly analogous to the `FromIterator` trait.
138pub trait GroupFromIterator<V> {
139 /// Builds a grouped collection from an iterator of key,value tuples.
140 /// Collections are extended in the order the values appear
141 /// in the iterator.
142 ///
143 /// It's recommended to call an iterator's `group()` method
144 /// rather than call this directly.
145 fn group_from_iter<I>(iter: I) -> Self
146 where
147 I: IntoIterator<Item = V>;
148}
149
150impl<K, V, C> GroupFromIterator<(K, V)> for C
151where
152 C: InsertAndExtend<K, V>,
153{
154 #[inline]
155 fn group_from_iter<I>(iter: I) -> Self
156 where
157 I: IntoIterator<Item = (K, V)>,
158 {
159 #[derive(Debug)]
160 enum Never {}
161
162 let r: Result<Self, Never> = GroupFromIterator::group_from_iter(iter.into_iter().map(Ok));
163 r.unwrap()
164 }
165}
166
167impl<K, V, C> GroupFromIterator<Option<(K, V)>> for Option<C>
168where
169 C: InsertAndExtend<K, V>,
170{
171 #[inline]
172 fn group_from_iter<I>(iter: I) -> Self
173 where
174 I: IntoIterator<Item = Option<(K, V)>>,
175 {
176 let r: Result<C, ()> =
177 GroupFromIterator::group_from_iter(iter.into_iter().map(|o| o.ok_or(())));
178 r.ok()
179 }
180}
181
182impl<K, V, C, E> GroupFromIterator<Result<(K, V), E>> for Result<C, E>
183where
184 C: InsertAndExtend<K, V>,
185{
186 fn group_from_iter<I>(iter: I) -> Self
187 where
188 I: IntoIterator<Item = Result<(K, V), E>>,
189 {
190 let mut h = C::default();
191
192 for r in iter {
193 let (k, v) = r?;
194 h.insert_and_extend(k, v);
195 }
196
197 Ok(h)
198 }
199}
200
201/// Extends Iterators with an additional grouping method.
202pub trait IntoGroup<V> {
203 /// Consumes the iterator and returns a grouping of its contents.
204 fn group<B: GroupFromIterator<V>>(self) -> B;
205}
206
207impl<V, I> IntoGroup<V> for I
208where
209 I: Iterator<Item = V>,
210{
211 #[inline]
212 fn group<B: GroupFromIterator<V>>(self) -> B {
213 B::group_from_iter(self)
214 }
215}
216
217/// A trait for mapping types to serve as a trivial wrapper
218/// over the Entry interface.
219pub trait InsertAndExtend<K, V>: Default {
220 /// Creates collection with the given key if necessary
221 /// and extends it with the given value.
222 fn insert_and_extend(&mut self, key: K, value: V);
223}
224
225impl<K, V, E, H> InsertAndExtend<K, V> for HashMap<K, E, H>
226where
227 K: Eq + Hash,
228 E: Default + Extend<V>,
229 H: BuildHasher + Default,
230{
231 #[inline]
232 fn insert_and_extend(&mut self, key: K, value: V) {
233 self.entry(key).or_default().extend(std::iter::once(value));
234 }
235}
236
237impl<K, V, E> InsertAndExtend<K, V> for BTreeMap<K, E>
238where
239 K: Ord,
240 E: Default + Extend<V>,
241{
242 #[inline]
243 fn insert_and_extend(&mut self, key: K, value: V) {
244 self.entry(key).or_default().extend(std::iter::once(value));
245 }
246}
247
248/// Implemented by mapping types (`HashMap` and `BTreeMap`)
249/// such that they can populated by an iterator.
250/// Roughly analogous to the `FromIterator` trait.
251pub trait SummarizeFromIterator<V> {
252 /// Builds a grouped collection from an iterator of key,value tuples.
253 ///
254 /// It's recommended to call an iterator's `summarize()` method
255 /// rather than call this directly.
256 fn summarize_from_iter<I>(iter: I) -> Self
257 where
258 I: IntoIterator<Item = V>;
259}
260
261impl<K, V, C> SummarizeFromIterator<(K, V)> for C
262where
263 C: InsertAndIncrement<K, V>,
264{
265 #[inline]
266 fn summarize_from_iter<I>(iter: I) -> Self
267 where
268 I: IntoIterator<Item = (K, V)>,
269 {
270 #[derive(Debug)]
271 enum Never {}
272
273 let r: Result<Self, Never> =
274 SummarizeFromIterator::summarize_from_iter(iter.into_iter().map(Ok));
275 r.unwrap()
276 }
277}
278
279impl<K, V, C> SummarizeFromIterator<Option<(K, V)>> for Option<C>
280where
281 C: InsertAndIncrement<K, V>,
282{
283 #[inline]
284 fn summarize_from_iter<I>(iter: I) -> Self
285 where
286 I: IntoIterator<Item = Option<(K, V)>>,
287 {
288 let r: Result<C, ()> =
289 SummarizeFromIterator::summarize_from_iter(iter.into_iter().map(|o| o.ok_or(())));
290 r.ok()
291 }
292}
293
294impl<K, V, C, E> SummarizeFromIterator<Result<(K, V), E>> for Result<C, E>
295where
296 C: InsertAndIncrement<K, V>,
297{
298 fn summarize_from_iter<I>(iter: I) -> Self
299 where
300 I: IntoIterator<Item = Result<(K, V), E>>,
301 {
302 let mut h = C::default();
303
304 for r in iter {
305 let (k, v) = r?;
306 h.insert_and_increment(k, v);
307 }
308
309 Ok(h)
310 }
311}
312
313/// Extends Iterators with an additional grouping method.
314pub trait IntoSummary<V> {
315 /// Consumes the iterator and returns a summarized of its contents.
316 fn summarize<B: SummarizeFromIterator<V>>(self) -> B;
317}
318
319impl<V, I> IntoSummary<V> for I
320where
321 I: Iterator<Item = V>,
322{
323 #[inline]
324 fn summarize<B: SummarizeFromIterator<V>>(self) -> B {
325 B::summarize_from_iter(self)
326 }
327}
328
329/// A trait for mapping types to serve as a trivial wrapper
330/// over the Entry interface
331pub trait InsertAndIncrement<K, V>: Default {
332 /// Creates collection with the given key if necessary
333 /// and increments its total by the given value
334 fn insert_and_increment(&mut self, key: K, value: V);
335}
336
337impl<K, V, H> InsertAndIncrement<K, V> for HashMap<K, V, H>
338where
339 K: Eq + Hash,
340 V: std::ops::AddAssign,
341 H: BuildHasher + Default,
342{
343 #[inline]
344 fn insert_and_increment(&mut self, key: K, value: V) {
345 use std::collections::hash_map::Entry;
346
347 match self.entry(key) {
348 Entry::Occupied(o) => *o.into_mut() += value,
349 Entry::Vacant(v) => {
350 v.insert(value);
351 }
352 }
353 }
354}
355
356impl<K, V> InsertAndIncrement<K, V> for BTreeMap<K, V>
357where
358 K: Ord,
359 V: std::ops::AddAssign,
360{
361 #[inline]
362 fn insert_and_increment(&mut self, key: K, value: V) {
363 use std::collections::btree_map::Entry;
364
365 match self.entry(key) {
366 Entry::Occupied(o) => *o.into_mut() += value,
367 Entry::Vacant(v) => {
368 v.insert(value);
369 }
370 }
371 }
372}