Skip to content

Instantly share code, notes, and snippets.

@hmayer00
Created February 18, 2020 21:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hmayer00/25ace4832ba85f228c76623a1f3fb0a1 to your computer and use it in GitHub Desktop.
Save hmayer00/25ace4832ba85f228c76623a1f3fb0a1 to your computer and use it in GitHub Desktop.
ObservableSetViews for filtering and sorting ObservableSets
import 'package:collection/collection.dart';
import 'package:mobx/mobx.dart';
typedef FilterPredicate<T> = bool Function(T element);
/// An unmodifiable but observable view of an ObservableSet.
///
/// This just delegates reads and observations to the [sourceSet].
class ObservableSetView<E> extends UnmodifiableSetView<E> implements ObservableSet<E> {
ObservableSetView(ObservableSet<E> sourceSet)
: _setBase = sourceSet,
super(sourceSet);
final ObservableSet<E> _setBase;
/// See [ObservableSet.observe]
@override
observe(listener, {bool fireImmediately}) =>
_setBase.observe(listener, fireImmediately: fireImmediately);
}
/// An unmodifiable, observable, filtering view of an ObservableSet.
///
/// This accepts a [predicate] and will filter the original set to a new [ObservableSet] containing
/// only elements for which the [predicate] returns true.
/// As the [sourceSet] changes, the filtered set will also change.
/// Listeners will only be notified when the filtered set changes (not on every change to the source).
class FilteringObservableSetView<E> extends ObservableSetView<E> {
/// Create an observable view of an [ObservableSet], which contains only elements matching the predicate.
FilteringObservableSetView.filteredFrom(ObservableSet<E> sourceSet, FilterPredicate<E> predicate)
: this._filtered(sourceSet, ObservableSet.of(sourceSet.where(predicate)), predicate);
// We have to use this private constructor so that we can use the non-static [_trackSource] while
// already having the filteredSet set up.
FilteringObservableSetView._filtered(
ObservableSet<E> sourceSet,
ObservableSet<E> filteredSet,
FilterPredicate<E> predicate
) : super(filteredSet) {
_trackSource(sourceSet, filteredSet, predicate);
}
/// A FilteringObservableSetView is observing the original set, and thus should be disposed of.
void dispose() => _disposer?.call();
Dispose _disposer;
void _trackSource(
ObservableSet<E> sourceSet,
ObservableSet<E> filteredSet,
FilterPredicate<E> predicate
) {
_disposer = sourceSet.observe((SetChange setChange) {
switch (setChange.type) {
case OperationType.add:
if (predicate(setChange.value)) filteredSet.add(setChange.value);
break;
case OperationType.remove:
filteredSet.remove(setChange.value);
break;
case OperationType.update:
throw UnsupportedError('SetChange should not have an update OperationType');
}
});
}
}
/// An unmodifiable, observable, sorting view of an ObservableSet.
///
/// This creates an observable [SplayTreeSet] to contain a sorted version of the [sourceSet],
/// and optionally accepts both [compare] and [isValidKey] (see [SplayTreeSet]).
///
/// As the [sourceSet] changes, the sorted set will also change.
class SortingObservableSetView<E> extends ObservableSetView<E> {
/// Create an observable view of an [ObservableSet], containing the same elements but sorted in
/// a [SplayTreeSet], with optional [compare] and [isValidKey] (see [SplayTreeSet])
SortingObservableSetView.sortedFrom(ObservableSet<E> sourceSet,
{ Comparator<E> compare, bool isValidKey(dynamic) })
: this._sorted(
sourceSet,
ObservableSet.splayTreeSetFrom(sourceSet, compare: compare, isValidKey: isValidKey)
);
// We have to use this private constructor so that we can use the non-static [_trackSource] while
// already having the sortedSet set up.
SortingObservableSetView._sorted(ObservableSet<E> sourceSet, ObservableSet<E> sortedSet)
: super(sortedSet) {
_trackSource(sourceSet, sortedSet);
}
/// A SortingObservableSetView is observing the original set, and thus should be disposed of.
void dispose() => _disposer?.call();
Dispose _disposer;
void _trackSource(ObservableSet<E> sourceSet, ObservableSet<E> sortedSet) {
_disposer = sourceSet.observe((SetChange setChange) {
switch (setChange.type) {
case OperationType.add:
sortedSet.add(setChange.value);
break;
case OperationType.remove:
sortedSet.remove(setChange.value);
break;
case OperationType.update:
throw UnsupportedError('SetChange should not have an update OperationType');
}
});
}
}
extension ObservableSetViews<E> on ObservableSet<E> {
/// Generate an [ObservableSetView] tracking this [ObservableSet].
ObservableSetView<E> unmodifiableView() {
return ObservableSetView<E>(this);
}
/// Generate a [FilteringObservableSetView] tracking this [ObservableSet].
FilteringObservableSetView<E> filteringView(FilterPredicate<E> predicate) {
return FilteringObservableSetView<E>.filteredFrom(this, predicate);
}
/// Generate a [SortingObservableSetView] tracking this [ObservableSet].
SortingObservableSetView<E> sortingView({Comparator<E> compare, bool isValidKey(dynamic)}) {
return SortingObservableSetView<E>.sortedFrom(this, compare: compare, isValidKey: isValidKey);
}
}
@hmayer00
Copy link
Author

On further review, I DO NOT RECOMMEND USING THIS CODE.

I'm leaving this here in case I or someone else has success with it, but going down this road I hit some pretty big performance bumps and i'm scrapping it instead of trying to figure out all of the details. The convenience wasn't worth the headache.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment