extension IterableExt on Iterable { Iterable separated(T separator) sync* { final iterator = this.iterator; if (!iterator.moveNext()) return; yield iterator.current; while (iterator.moveNext()) { yield separator; yield iterator.current; } } Iterable> chunks(int size) sync* { if (length == 0) return; var iterator = this.iterator; while (iterator.moveNext()) { var chunk = [iterator.current]; for (var i = 1; i < size && iterator.moveNext(); i++) { chunk.add(iterator.current); } yield chunk; } } Iterable fill(int length, {required T Function(int count) filler}) sync* { int count = 0; for (var item in this) { yield item; count++; if (count >= length) return; } while (count < length) { yield filler(count); count++; } } Iterable takeLast({int count = 50}) { if (count <= 0) return Iterable.empty(); return count >= length ? this : toList().skip(length - count); } } extension ListExt on List { void truncate(int maxLength) { if (maxLength == 0) { return; } if (length > maxLength) { removeRange(0, length - maxLength); } } List intersection(List list) { return where((item) => list.contains(item)).toList(); } List> batch(int maxConcurrent) { final batches = (length / maxConcurrent).ceil(); final List> res = []; for (int i = 0; i < batches; i++) { if (i != batches - 1) { res.add(sublist(i * maxConcurrent, maxConcurrent * (i + 1))); } else { res.add(sublist(i * maxConcurrent, length)); } } return res; } List safeSublist(int start, [int? end]) { if (start <= 0) return this; if (start > length) return []; if (end != null) { return sublist(start, end.clamp(start, length)); } return sublist(start); } T safeGet(int index) { if (length > index) return this[index]; return last; } T safeLast(T value) { if (isNotEmpty) { return last; } return value; } void addOrRemove(T value) { if (contains(value)) { remove(value); } else { add(value); } } } extension SetExt on Set { void addOrRemove(T value) { if (contains(value)) { remove(value); } else { add(value); } } } extension DoubleListExt on List { int findInterval(num target) { if (isEmpty) return -1; if (target < first) return -1; if (target >= last) return length - 1; int left = 0; int right = length - 1; while (left <= right) { int mid = left + (right - left) ~/ 2; if (mid == length - 1 || (this[mid] <= target && target < this[mid + 1])) { return mid; } else if (target < this[mid]) { right = mid - 1; } else { left = mid + 1; } } return -1; } } extension MapExt on Map { V updateCacheValue(K key, V Function() callback) { if (this[key] == null) { this[key] = callback(); } return this[key]!; } Map copyWitUpdate(K key, V? value) { final newMap = Map.from(this); if (value == null) { newMap.remove(key); } else { newMap[key] = value; } return newMap; } }