platform/packages/collections/lib/src/collection.dart

263 lines
8.2 KiB
Dart
Raw Normal View History

2024-12-15 23:26:11 +00:00
import 'dart:collection';
import 'dart:math';
/// A collection class inspired by Laravel's Collection, implemented in Dart.
class Collection<T> with ListMixin<T> {
final List<T> _items;
/// Creates a new [Collection] instance.
Collection([Iterable<T>? items]) : _items = List<T>.from(items ?? <T>[]);
/// Creates a new [Collection] instance from a [Map].
factory Collection.fromMap(Map<dynamic, T> map) {
return Collection(map.values);
}
@override
int get length => _items.length;
@override
set length(int newLength) {
_items.length = newLength;
}
@override
T operator [](int index) => _items[index];
@override
void operator []=(int index, T value) {
_items[index] = value;
}
/// Returns all items in the collection.
List<T> all() => _items.toList();
/// Returns the average value of the collection.
double? avg([num Function(T element)? callback]) {
if (isEmpty) return null;
num sum = 0;
for (final item in _items) {
sum += callback != null ? callback(item) : (item as num);
}
return sum / length;
}
/// Chunks the collection into smaller collections of a given size.
Collection<Collection<T>> chunk(int size) {
return Collection(
List.generate(
(length / size).ceil(),
(index) => Collection(_items.skip(index * size).take(size)),
),
);
}
/// Collapses a collection of arrays into a single, flat collection.
Collection<dynamic> collapse() {
return Collection(_items.expand((e) => e is Iterable ? e : [e]));
}
/// Determines whether the collection contains a given item.
@override
bool contains(Object? item) => _items.contains(item);
/// Returns the total number of items in the collection.
int count() => length;
/// Executes a callback over each item.
void each(void Function(T item) callback) {
for (final item in _items) {
callback(item);
}
}
/// Creates a new collection consisting of every n-th element.
Collection<T> everyNth(int step) {
return Collection(_items.where((item) => _items.indexOf(item) % step == 0));
}
/// Returns all items except for those with the specified keys.
Collection<T> except(List<int> keys) {
return Collection(
_items.where((item) => !keys.contains(_items.indexOf(item))));
}
/// Filters the collection using the given callback.
Collection<T> whereCustom(bool Function(T element) test) {
return Collection(_items.where(test));
}
/// Returns the first element in the collection that passes the given truth test.
@override
T firstWhere(bool Function(T element) test, {T Function()? orElse}) {
return _items.firstWhere(test, orElse: orElse);
}
/// Flattens a multi-dimensional collection into a single dimension.
Collection<dynamic> flatten({int depth = 1}) {
List<dynamic> flattenHelper(dynamic item, int currentDepth) {
if (currentDepth == 0 || item is! Iterable) return [item];
return item.expand((e) => flattenHelper(e, currentDepth - 1)).toList();
}
return Collection(
flattenHelper(_items, depth).expand((e) => e is Iterable ? e : [e]));
}
/// Flips the items in the collection.
Collection<T> flip() {
return Collection(_items.reversed);
}
/// Removes an item from the collection by its key.
T? pull(int index) {
if (index < 0 || index >= length) return null;
return removeAt(index);
}
/// Concatenates the given array or collection with the original collection.
Collection<T> concat(Iterable<T> items) {
return Collection([..._items, ...items]);
}
/// Reduces the collection to a single value.
@override
U fold<U>(U initialValue, U Function(U previousValue, T element) combine) {
return _items.fold(initialValue, combine);
}
/// Groups the collection's items by a given key.
Map<K, List<T>> groupBy<K>(K Function(T element) keyFunction) {
return _items.fold<Map<K, List<T>>>({}, (map, element) {
final key = keyFunction(element);
map.putIfAbsent(key, () => []).add(element);
return map;
});
}
/// Joins the items in a collection with a string.
@override
String join([String separator = '']) => _items.join(separator);
/// Returns a new collection with the keys of the collection items.
Collection<int> keys() => Collection(List.generate(length, (index) => index));
/// Returns the last element in the collection.
T? lastOrNull() => isNotEmpty ? _items.last : null;
/// Runs a map over each of the items.
Collection<R> mapCustom<R>(R Function(T e) toElement) {
return Collection(_items.map(toElement));
}
/// Run a map over each nested chunk of items.
Collection<R> mapSpread<R>(R Function(dynamic e) toElement) {
return Collection(_items
.expand((e) => e is Iterable ? e.map(toElement) : [toElement(e)]));
}
/// Returns the maximum value of a given key.
T? max([Comparable<dynamic> Function(T element)? callback]) {
if (isEmpty) return null;
return _items.reduce((a, b) {
final compareA =
callback != null ? callback(a) : a as Comparable<dynamic>;
final compareB =
callback != null ? callback(b) : b as Comparable<dynamic>;
return compareA.compareTo(compareB) > 0 ? a : b;
});
}
/// Returns the minimum value of a given key.
T? min([Comparable<dynamic> Function(T element)? callback]) {
if (isEmpty) return null;
return _items.reduce((a, b) {
final compareA =
callback != null ? callback(a) : a as Comparable<dynamic>;
final compareB =
callback != null ? callback(b) : b as Comparable<dynamic>;
return compareA.compareTo(compareB) < 0 ? a : b;
});
}
/// Returns only the items from the collection with the specified keys.
Collection<T> only(List<int> keys) {
return Collection(
_items.where((item) => keys.contains(_items.indexOf(item))));
}
/// Retrieves all of the collection values for a given key.
Collection<R> pluck<R>(R Function(T element) callback) {
return Collection(_items.map(callback));
}
/// Removes and returns the last item from the collection.
T? pop() => isNotEmpty ? removeLast() : null;
/// Adds an item to the beginning of the collection.
void prepend(T value) => insert(0, value);
/// Adds an item to the end of the collection.
void push(T value) => add(value);
/// Returns a random item from the collection.
T? random() => isEmpty ? null : this[_getRandomIndex()];
/// Reverses the order of the collection's items.
Collection<T> reverse() => Collection(_items.reversed);
/// Searches the collection for a given value and returns the corresponding key if successful.
int? search(T item, {bool Function(T, T)? compare}) {
compare ??= (a, b) => a == b;
final index = _items.indexWhere((element) => compare!(element, item));
return index != -1 ? index : null;
}
/// Shuffles the items in the collection.
@override
void shuffle([Random? random]) {
_items.shuffle(random);
}
/// Returns a slice of the collection starting at the given index.
Collection<T> slice(int offset, [int? length]) {
return Collection(
_items.skip(offset).take(length ?? _items.length - offset));
}
/// Sorts the collection.
Collection<T> sortCustom([int Function(T a, T b)? compare]) {
final sorted = [..._items];
sorted.sort(compare);
return Collection(sorted);
}
/// Takes the first or last {n} items.
@override
Collection<T> take(int count) {
if (count < 0) {
return Collection(_items.skip(_items.length + count));
}
return Collection(_items.take(count));
}
/// Returns a JSON representation of the collection.
String toJson() =>
'[${_items.map((e) => e is Map ? _mapToJson(e as Map<dynamic, dynamic>) : e.toString()).join(',')}]';
/// Merges the given array or collection with the original collection.
Collection<T> merge(Iterable<T> items) {
return Collection([..._items, ...items]);
}
// Helper methods
int _getRandomIndex() =>
(DateTime.now().millisecondsSinceEpoch % length).abs();
String _mapToJson(Map<dynamic, dynamic> map) {
final pairs = map.entries.map((e) =>
'"${e.key}":${e.value is Map ? _mapToJson(e.value as Map<dynamic, dynamic>) : '"${e.value}"'}');
return '{${pairs.join(',')}}';
}
}