aggregate_map/lib.rs
1//! Collect a list of key-value pairs into a mapping of keys to collections of values.
2//!
3//! If you have a set of data that you want to collect into a map, by default you'll only keep the
4//! last value in the data for that key. But what if you want instead to keep a collection of all
5//! the values for each key? Enter [`AggregateMap`]!
6//!
7//!
8//! ```rust
9//! # use std::collections::HashMap;
10//! # use aggregate_map::AggregateMap;
11//! let data = [
12//! ("dog", "Terry"),
13//! ("dog", "Zamboni"),
14//! ("cat", "Jonathan"),
15//! ("dog", "Priscilla"),
16//! ];
17//! let collected: AggregateMap<HashMap<_, Vec<_>>> = data.into_iter().collect();
18//! let expected = HashMap::from([
19//! ("dog", vec!["Terry", "Zamboni", "Priscilla"]),
20//! ("cat", vec!["Jonathan"])
21//! ]);
22//! assert_eq!(collected.into_inner(), expected);
23//! ```
24//!
25//! [`AggregateMap`] can be used with any map type that implements this crate's [`Map`] trait, such
26//! as [`HashMap`][std::collections::HashMap] or [`BTreeMap`][std::collections::BTreeMap].
27//!
28//! The collection type doesn't have to be a [`Vec`], too, it can be anything that implements
29//! [`Extend`] and [`Default`]. For instance, here's an example with a
30//! [`HashSet`][std::collections::HashSet]:
31//! ```rust
32//! # use std::collections::{HashMap, HashSet};
33//! # use aggregate_map::AggregateMap;
34//! let data = [
35//! ("dog", "Terry"),
36//! ("dog", "Terry"),
37//! ("dog", "Priscilla"),
38//! ];
39//! let collected: AggregateMap<HashMap<_, HashSet<_>>> = data.into_iter().collect();
40//! let expected = HashMap::from([
41//! ("dog", HashSet::from(["Terry", "Priscilla"])),
42//! ]);
43//! assert_eq!(collected.into_inner(), expected);
44//! ```
45//!
46//! It can even be another [`AggregateMap`] for additional levels of aggregation!
47//! ```rust
48//! # use std::collections::HashMap;
49//! # use aggregate_map::AggregateMap;
50//! let data = [
51//! ("pet", ("dog", "Terry")),
52//! ("pet", ("dog", "Priscilla")),
53//! ("stray", ("cat", "Jennifer")),
54//! ("pet", ("cat", "Absalom")),
55//! ];
56//! let collected: AggregateMap<HashMap<_, AggregateMap<HashMap<_, Vec<_>>>>> =
57//! data.into_iter().collect();
58//! let expected = HashMap::from([
59//! ("pet", HashMap::from([
60//! ("dog", vec!["Terry", "Priscilla"]),
61//! ("cat", vec!["Absalom"]),
62//! ])),
63//! ("stray", HashMap::from([
64//! ("cat", vec!["Jennifer"]),
65//! ])),
66//! ]);
67//! let collected: HashMap<_, _> = collected
68//! .into_inner()
69//! .into_iter()
70//! .map(|(key, map)| (key, map.into_inner()))
71//! .collect();
72//! assert_eq!(collected, expected);
73//! ```
74#![cfg_attr(docsrs, feature(doc_cfg))]
75#![forbid(unsafe_code)]
76#![warn(clippy::pedantic)]
77
78
79#[cfg(feature = "btreemap")]
80#[cfg_attr(docsrs, doc(cfg(feature = "btreemap")))]
81pub mod btreemap;
82#[cfg(feature = "hashmap")]
83pub mod hashmap;
84
85/// A wrapper around a "map" type that lets you collect an iterator of key-value pairs into a
86/// mapping between keys and collections of values, instead of just keys to values.
87#[derive(Default, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
89pub struct AggregateMap<M>(M);
90
91impl<M> AggregateMap<M> {
92 /// Consumes the [`AggregateMap`] to give you the inner map `M`.
93 pub fn into_inner(self) -> M {
94 self.0
95 }
96}
97
98impl<M> AsRef<M> for AggregateMap<M> {
99 fn as_ref(&self) -> &M {
100 &self.0
101 }
102}
103
104impl<M> AsMut<M> for AggregateMap<M> {
105 fn as_mut(&mut self) -> &mut M {
106 &mut self.0
107 }
108}
109impl<M> From<M> for AggregateMap<M> {
110 fn from(map: M) -> Self {
111 Self(map)
112 }
113}
114
115/// A trait for "map" types (such as [`HashMap`][std::collections::HashMap]) that you can collect
116/// into with an [`AggregateMap`].
117///
118/// Implementations of this trait are provided for `std` maps, but if you have a custom map type you
119/// can implement this trait for it to be able to use it with [`AggregateMap`].
120///
121/// Implementors of this trait will generally have a key of `K`, but a value of some collection type
122/// (like [`Vec`] or [`HashSet`][std::collections::HashSet]), which contains multiple values of type
123/// `V`.
124pub trait Map<K, V> {
125 /// Insert one `value` into the collection contained at `key`.
126 fn insert(&mut self, key: K, value: V);
127}
128
129impl<M, K, V> Extend<(K, V)> for AggregateMap<M>
130where
131 M: Map<K, V>,
132{
133 fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
134 iter.into_iter()
135 .for_each(|(key, value)| self.0.insert(key, value));
136 }
137}
138
139impl<M, K, V> FromIterator<(K, V)> for AggregateMap<M>
140where
141 M: Map<K, V> + Default,
142{
143 fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
144 let mut this = Self::default();
145 this.extend(iter);
146 this
147 }
148}