use std::collections::{
hash_map::{Entry, HashMap},
HashSet,
};
use std::hash::Hash;
pub trait GroupingBy {
type GItem;
fn grouping_by<K, F>(self, key: F) -> HashMap<K, Vec<Self::GItem>>
where
F: FnMut(&Self::GItem) -> K,
K: Eq + Hash;
fn grouping_by_as_set<K, F>(self, key: F) -> HashMap<K, HashSet<Self::GItem>>
where
Self::GItem: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
K: Eq + Hash;
fn counter<K, F>(self, key: F) -> HashMap<K, usize>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K;
fn grouping_by_max<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering;
fn grouping_by_min<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering;
fn summing<K, V, F, G>(self, key: F, sum_func: G) -> HashMap<K, V>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
G: FnMut(&Self::GItem) -> V,
V: Default + std::ops::AddAssign;
}
mod utilities {
use super::{Entry, Hash, HashMap};
pub fn grouping_by_min_max_aux<T, K, F, C>(
iterator: T,
mut key: F,
mut comparator: C,
type_ord: std::cmp::Ordering,
) -> HashMap<K, T::Item>
where
T: Iterator,
K: Eq + Hash,
F: FnMut(&T::Item) -> K,
C: FnMut(&T::Item, &T::Item) -> std::cmp::Ordering,
{
let mut map = HashMap::new();
iterator.for_each(|item| {
let key = key(&item);
match map.entry(key) {
Entry::Occupied(mut entry) => {
if comparator(&item, entry.get()) == type_ord {
entry.insert(item);
}
}
Entry::Vacant(entry) => {
entry.insert(item);
}
}
});
map
}
}
impl<T: Iterator> GroupingBy for T {
type GItem = T::Item;
fn grouping_by<K, F>(self, mut key: F) -> HashMap<K, Vec<Self::GItem>>
where
F: FnMut(&Self::GItem) -> K,
K: Eq + Hash,
{
let mut map = HashMap::new();
self.for_each(|item| {
map.entry(key(&item)).or_insert_with(Vec::new).push(item);
});
map
}
fn grouping_by_as_set<K, F>(self, mut key: F) -> HashMap<K, HashSet<Self::GItem>>
where
Self::GItem: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
K: Eq + Hash,
{
let mut map = HashMap::new();
self.for_each(|item| {
map.entry(key(&item))
.or_insert_with(HashSet::new)
.insert(item);
});
map
}
fn counter<K, F>(self, mut key: F) -> HashMap<K, usize>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
{
let mut map = HashMap::new();
self.for_each(|item| {
*map.entry(key(&item)).or_insert(0) += 1;
});
map
}
fn grouping_by_max<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering,
{
utilities::grouping_by_min_max_aux(self, key, comparator, std::cmp::Ordering::Greater)
}
fn grouping_by_min<K, F, C>(self, key: F, comparator: C) -> HashMap<K, Self::GItem>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
C: FnMut(&Self::GItem, &Self::GItem) -> std::cmp::Ordering,
{
utilities::grouping_by_min_max_aux(self, key, comparator, std::cmp::Ordering::Less)
}
fn summing<K, V, F, G>(self, mut key: F, mut value: G) -> HashMap<K, V>
where
K: Eq + Hash,
F: FnMut(&Self::GItem) -> K,
G: FnMut(&Self::GItem) -> V,
V: Default + std::ops::AddAssign,
{
let mut map: HashMap<K, V> = HashMap::new();
self.for_each(|item| {
let v = map.entry(key(&item)).or_default();
*v += value(&item);
});
map
}
}