Skip to content

Instantly share code, notes, and snippets.

@BSN4
Created February 23, 2025 12:45
Show Gist options
  • Save BSN4/02e2d9ff20cc9b1c1ef40d647a70938e to your computer and use it in GitHub Desktop.
Save BSN4/02e2d9ff20cc9b1c1ef40d647a70938e to your computer and use it in GitHub Desktop.
laravel collection in rust
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
pub struct Collection<'a, T> {
items: Vec<&'a T>,
}
impl<'a, T> Collection<'a, T> {
pub fn new(items: &'a [T]) -> Self {
Self {
items: items.iter().collect(),
}
}
// Basic operations
pub fn filter(self, predicate: impl Fn(&T) -> bool) -> Self {
Self {
items: self
.items
.into_iter()
.filter(|item| predicate(item))
.collect(),
}
}
pub fn filter_map<B>(self, f: impl FnMut(&T) -> Option<&'a B>) -> Collection<'a, B> {
Collection {
items: self.items.into_iter().filter_map(f).collect(),
}
}
pub fn map<B>(self, f: impl FnMut(&T) -> &'a B) -> Collection<'a, B> {
Collection {
items: self.items.into_iter().map(f).collect(),
}
}
// Laravel-style operations
pub fn chunk(self, size: usize) -> Vec<Collection<'a, T>> {
self.items
.chunks(size)
.map(|chunk| Collection {
items: chunk.to_vec(),
})
.collect()
}
pub fn take(self, limit: usize) -> Self {
Self {
items: self.items.into_iter().take(limit).collect(),
}
}
pub fn skip(self, count: usize) -> Self {
Self {
items: self.items.into_iter().skip(count).collect(),
}
}
pub fn unique_by<K: Hash + Eq>(self, key_fn: impl Fn(&T) -> K) -> Self {
let mut seen = HashSet::new();
Self {
items: self
.items
.into_iter()
.filter(|item| seen.insert(key_fn(item)))
.collect(),
}
}
pub fn unique(self) -> Self
where
T: Hash + Eq,
{
let mut seen = HashSet::new();
Self {
items: self
.items
.into_iter()
.filter(|item| seen.insert(*item))
.collect(),
}
}
pub fn sort_by<K: Ord>(self, key_fn: impl Fn(&T) -> K) -> Self {
let mut items = self.items;
items.sort_by_key(|item| key_fn(item));
Self { items }
}
pub fn sort_by_desc<K: Ord>(self, key_fn: impl Fn(&T) -> K) -> Self {
let mut items = self.items;
items.sort_by_key(|item| std::cmp::Reverse(key_fn(item)));
Self { items }
}
pub fn reverse(self) -> Self {
let mut items = self.items;
items.reverse();
Self { items }
}
pub fn group_by<K>(self, key_fn: impl Fn(&T) -> K) -> GroupedCollection<'a, K, T>
where
K: Eq + Hash,
{
let groups = self
.items
.into_iter()
.fold(HashMap::new(), |mut acc, item| {
acc.entry(key_fn(item)).or_insert_with(Vec::new).push(item);
acc
});
GroupedCollection { groups }
}
// Reduction operations
pub fn reduce<B>(self, init: B, f: impl Fn(B, &T) -> B) -> B {
self.items.into_iter().fold(init, f)
}
pub fn each(self, f: impl Fn(&T)) -> Self {
self.items.iter().for_each(|item| f(item));
self
}
pub fn tap<F>(self, f: F) -> Self
where
F: FnOnce(&Collection<'a, T>),
{
f(&self);
self
}
// Set operations
pub fn intersect(self, other: Collection<'a, T>) -> Self
where
T: Eq + Hash,
{
let other_set: HashSet<_> = other.items.into_iter().collect();
Self {
items: self
.items
.into_iter()
.filter(|item| other_set.contains(item))
.collect(),
}
}
pub fn diff(self, other: Collection<'a, T>) -> Self
where
T: Eq + Hash,
{
let other_set: HashSet<_> = other.items.into_iter().collect();
Self {
items: self
.items
.into_iter()
.filter(|item| !other_set.contains(item))
.collect(),
}
}
pub fn union(self, other: Collection<'a, T>) -> Self
where
T: Eq + Hash,
{
let mut seen = HashSet::new();
let mut items = Vec::new();
for item in self.items.into_iter().chain(other.items.into_iter()) {
if seen.insert(item) {
items.push(item);
}
}
Self { items }
}
// Aggregation operations
pub fn contains(&self, predicate: impl Fn(&T) -> bool) -> bool {
self.items.iter().any(|item| predicate(item))
}
pub fn every(&self, predicate: impl Fn(&T) -> bool) -> bool {
self.items.iter().all(|item| predicate(item))
}
pub fn sum_by<R>(self, f: impl Fn(&T) -> R) -> R
where
R: Default + std::ops::AddAssign,
{
self.items.into_iter().fold(R::default(), |mut acc, item| {
acc += f(item);
acc
})
}
pub fn average_by(self, f: impl Fn(&T) -> f64) -> Option<f64> {
if self.items.is_empty() {
None
} else {
let len = self.items.len();
let sum: f64 = self.items.into_iter().map(|item| f(item)).sum();
Some(sum / len as f64)
}
}
pub fn median_by<R>(self, f: impl Fn(&T) -> R) -> Option<R>
where
R: Ord + Clone,
{
if self.items.is_empty() {
return None;
}
let mut values: Vec<_> = self.items.into_iter().map(|item| f(item)).collect();
values.sort();
let mid = values.len() / 2;
Some(values[mid].clone())
}
// Terminal operations
pub fn get(self) -> Vec<&'a T> {
self.items
}
pub fn all(self) -> Vec<&'a T> {
self.items
}
pub fn first(self) -> Option<&'a T> {
self.items.into_iter().next()
}
pub fn last(self) -> Option<&'a T> {
self.items.into_iter().last()
}
pub fn nth(self, n: usize) -> Option<&'a T> {
self.items.into_iter().nth(n)
}
pub fn count(self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
// Pagination support
pub fn paginate(self, page: usize, per_page: usize) -> PaginatedCollection<'a, T> {
let total = self.items.len();
let items = self
.items
.into_iter()
.skip((page - 1) * per_page)
.take(per_page)
.collect();
PaginatedCollection {
items,
total,
per_page,
current_page: page,
}
}
}
pub struct GroupedCollection<'a, K, T> {
groups: HashMap<K, Vec<&'a T>>,
}
impl<'a, K, T> GroupedCollection<'a, K, T>
where
K: Eq + Hash,
{
pub fn all(self) -> HashMap<K, Vec<&'a T>> {
self.groups
}
pub fn get(self, key: K) -> Option<Vec<&'a T>> {
self.groups.get(&key).cloned()
}
pub fn map<B>(self, f: impl Fn(Vec<&'a T>) -> B) -> HashMap<K, B> {
self.groups.into_iter().map(|(k, v)| (k, f(v))).collect()
}
}
pub struct PaginatedCollection<'a, T> {
items: Vec<&'a T>,
total: usize,
per_page: usize,
current_page: usize,
}
impl<'a, T> PaginatedCollection<'a, T> {
pub fn items(self) -> Vec<&'a T> {
self.items
}
pub fn total(&self) -> usize {
self.total
}
pub fn per_page(&self) -> usize {
self.per_page
}
pub fn current_page(&self) -> usize {
self.current_page
}
pub fn total_pages(&self) -> usize {
(self.total + self.per_page - 1) / self.per_page
}
pub fn has_more_pages(&self) -> bool {
self.current_page < self.total_pages()
}
}
#[derive(Debug, Default)]
pub struct QueryableCollection<T> {
items: Vec<T>,
}
impl<T> QueryableCollection<T> {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
items: Vec::with_capacity(capacity),
}
}
pub fn add(&mut self, item: T) {
self.items.push(item);
}
pub fn query(&self) -> Collection<T> {
Collection::new(&self.items)
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
}
// Example usage
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq)]
struct Flight {
registration: String,
aircraft_type: String,
duration: i32,
fuel: f64,
}
#[test]
fn test_laravel_style_operations() {
let mut flights = QueryableCollection::new();
let flight1 = Flight {
registration: "N-12345".to_string(),
aircraft_type: "C172".to_string(),
duration: 120,
fuel: 45.5,
};
let flight2 = Flight {
registration: "N-54321".to_string(),
aircraft_type: "C182".to_string(),
duration: 90,
fuel: 35.0,
};
flights.add(flight1);
flights.add(flight2);
// Test unique
let unique_types = flights
.query()
.unique_by(|f| f.aircraft_type.clone())
.count();
assert_eq!(unique_types, 2);
// Test sorting
let sorted = flights.query().sort_by(|f| f.duration).get();
assert_eq!(sorted[0].duration, 90);
// Test pagination
let page = flights.query().paginate(1, 1);
assert_eq!(page.items().len(), 1);
// assert_eq!(page.total_pages(), 2); page moved to .items() in prev statment can't be used again
// Test chaining multiple operations
let result = flights
.query()
.filter(|f| f.duration > 60)
.sort_by_desc(|f| f.duration)
.take(1)
.get();
assert_eq!(result.len(), 1);
assert_eq!(result[0].duration, 120);
// Test tap for debugging
flights.query().filter(|f| f.duration > 100).get();
}
}
fn main() {
println!(".....");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment