use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ModelCollection<T>(Vec<T>);
impl<T> ModelCollection<T> {
pub fn new(items: Vec<T>) -> Self {
Self(items)
}
pub fn into_vec(self) -> Vec<T> {
self.0
}
pub fn as_slice(&self) -> &[T] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn count(&self) -> usize {
self.0.len()
}
pub fn filter<F>(self, predicate: F) -> Self
where
F: FnMut(&T) -> bool,
{
Self(self.0.into_iter().filter(predicate).collect())
}
pub fn reject<F>(self, mut predicate: F) -> Self
where
F: FnMut(&T) -> bool,
{
Self(self.0.into_iter().filter(|item| !predicate(item)).collect())
}
pub fn find<F>(&self, predicate: F) -> Option<&T>
where
F: FnMut(&&T) -> bool,
{
self.0.iter().find(predicate)
}
pub fn contains<F>(&self, predicate: F) -> bool
where
F: FnMut(&T) -> bool,
{
self.0.iter().any(predicate)
}
pub fn first(&self) -> Option<&T> {
self.0.first()
}
pub fn last(&self) -> Option<&T> {
self.0.last()
}
pub fn map<U, F>(self, f: F) -> Vec<U>
where
F: FnMut(T) -> U,
{
self.0.into_iter().map(f).collect()
}
pub fn flat_map<U, I, F>(self, f: F) -> Vec<U>
where
F: FnMut(T) -> I,
I: IntoIterator<Item = U>,
{
self.0.into_iter().flat_map(f).collect()
}
pub fn pluck<U, F>(&self, f: F) -> Vec<U>
where
F: FnMut(&T) -> U,
{
self.0.iter().map(f).collect()
}
pub fn group_by<K, F>(self, mut key_fn: F) -> HashMap<K, Vec<T>>
where
K: std::hash::Hash + Eq,
F: FnMut(&T) -> K,
{
let mut map: HashMap<K, Vec<T>> = HashMap::new();
for item in self.0 {
map.entry(key_fn(&item)).or_default().push(item);
}
map
}
pub fn key_by<K, F>(self, mut key_fn: F) -> HashMap<K, T>
where
K: std::hash::Hash + Eq,
F: FnMut(&T) -> K,
{
let mut map: HashMap<K, T> = HashMap::new();
for item in self.0 {
let k = key_fn(&item);
map.insert(k, item);
}
map
}
pub fn chunk(self, size: usize) -> Vec<Vec<T>>
where
T: Clone,
{
self.0
.into_iter()
.collect::<Vec<_>>()
.chunks(size)
.map(|c| c.to_vec())
.collect()
}
pub fn unique_by<K, F>(self, mut key_fn: F) -> Self
where
K: std::hash::Hash + Eq,
F: FnMut(&T) -> K,
{
let mut seen: std::collections::HashSet<K> = std::collections::HashSet::new();
let filtered: Vec<T> = self
.0
.into_iter()
.filter(|item| seen.insert(key_fn(item)))
.collect();
Self(filtered)
}
pub fn sort_by<F>(mut self, compare: F) -> Self
where
F: FnMut(&T, &T) -> std::cmp::Ordering,
{
self.0.sort_by(compare);
self
}
pub fn sum<F>(&self, mut f: F) -> f64
where
F: FnMut(&T) -> f64,
{
self.0.iter().map(&mut f).sum()
}
pub fn avg<F>(&self, f: F) -> Option<f64>
where
F: FnMut(&T) -> f64,
{
if self.0.is_empty() {
return None;
}
Some(self.sum(f) / self.0.len() as f64)
}
pub fn min_by<U, F>(&self, mut f: F) -> Option<&T>
where
U: Ord,
F: FnMut(&T) -> U,
{
self.0.iter().min_by_key(|item| f(item))
}
pub fn max_by<U, F>(&self, mut f: F) -> Option<&T>
where
U: Ord,
F: FnMut(&T) -> U,
{
self.0.iter().max_by_key(|item| f(item))
}
pub fn where_field<U, F>(self, field_fn: F, value: U) -> Self
where
U: PartialEq,
F: Fn(&T) -> U,
{
Self(
self.0
.into_iter()
.filter(|item| field_fn(item) == value)
.collect(),
)
}
pub fn where_in_field<U, F>(self, field_fn: F, values: &[U]) -> Self
where
U: PartialEq,
F: Fn(&T) -> U,
{
Self(
self.0
.into_iter()
.filter(|item| values.contains(&field_fn(item)))
.collect(),
)
}
}
impl<T> From<Vec<T>> for ModelCollection<T> {
fn from(v: Vec<T>) -> Self {
Self(v)
}
}
impl<T> From<ModelCollection<T>> for Vec<T> {
fn from(c: ModelCollection<T>) -> Self {
c.0
}
}
impl<T> IntoIterator for ModelCollection<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a, T> IntoIterator for &'a ModelCollection<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
pub trait IntoCollection<T> {
fn into_collection(self) -> ModelCollection<T>;
}
impl<T> IntoCollection<T> for Vec<T> {
fn into_collection(self) -> ModelCollection<T> {
ModelCollection::new(self)
}
}